Квалификаторы Const
 
Примечание: Как и с областями видимости, квалификаторы Const может быть немного трудно понять. Вы должны иметь глубокое понимание областей действия переменной перед попыткой понять квалификаторы Const.

Что такое квалификаторы Const? Квалификаторы Const являются функциональностью, недавно добавленной в язык (fbc 0.18.3); они являются стандартной частью C++, и теперь они существуют в FreeBasic тоже. Квалификаторы Const являются еще одной формой защиты - они позволяют некоторым «переменным» действовать как константы в определенных частях вашей программы, иными словами некоторым частям программы разрешается доступ (чтение) к ним, но без возможности изменения. По сути , просто еще один вид безопасного типа, являющегося чрезвычайно полезным. В частности они очень полезны в ООП ситуациях, но вы вероятно все равно будете пользоваться ими, даже если вы не заинтересованы в ООП.

Квалификатор Const в FreeBasic является по сути продолжением для объявления типов данных, и они могут быть использованы с Dim, элементами UDT и параметрами процедур. Как правило , данный квалификатор пишется после ключевого  слова "As" и является частью объявления переменных:

Dim As Const Integer my_const_int = 5


(Кстати, на протяжении этого урока я использую только типы Integer и Integer ptr, однако, квалификаторы Const должны работать со всеми другими типами переменных, включая Types, Enums и др. Если по каким-либо причинам этого не произошло, вероятно, это ошибка и вы должны сообщить об этом.)

Обратите внимание, что позволяется изменить ее только один раз при инициализации, но после этого изменить ее нет никакой возможности. Инициализация обязательна, иначе компилятор выдаст ошибку, например если вы после объявлении сделаете что-то типа этого:

my_const_int = 3


Тем не менее , хоть ее и нельзя менять, но вы можете читать ее:

Print my_const_int


Все это конечно же очень хорошо, но по сути не сильно отличается от нормального использования Const. То есть следующие две строки фактически делают тоже самое:

Dim As Const Integer my_const_int = 5
Const my_int As Integer = 5


И что же получается, они одинаковы? Не совсем. Вы видите, квалификатор Const , который позволяет создавать константу из переменной, но по сути они действуют как переменные, за исключением того, что они не могут быть изменены. Это означает, что вы можете поместить их в типы и другие места. Более того вы можете поместить их внутрь объявления Sub/Function - и это ключевая причина для их существования.

Sub my_sub (some_num As Integer)
End Sub


Обычно функции разрешают изменения переменных, которые в них посланы. Конечно же, изменение первоначальной переменной или просто локальной копии переменной зависит от используемого спецификатора ByVal или ByRef (и конечно есть возможность изменения по указателю, но это вообще другая тема), но обычно стоит разрешение для изменения переменной. Это может быть нежелательным по какой-то причине, и существует квалификатор Const, чтобы предотвратить это. В функции, приведенной выше some_num может быть изменена с помощью функции. Это конечно же поменяет локальную копию, и это хорошо, так как это не повлияет на оригинальный Const Integer, но что делать, если мы объявим функцию так, как указано ниже?

Sub my_sub (ByRef some_num As Integer)
End Sub


Теперь my_sub имеет прямой доступ к любой переменной, и послав в процедуру Const Integer ничего не выйдет:

my_sub(my_const_int)


Почему? Просто потому, что функция имеет разрешение менять переменную. А константы менять нельзя. Самое главное, что в процессе разработки может понадобиться посылать туда не только обычные переменные , но и константные переменные. В примере выше компилятор сообщит об ошибке: "Invalid assignment/conversion."

А вот если бы сделали что-то вроде этого:

Sub my_sub (ByRef some_num As Const Integer)
End Sub


Тогда все отлично , но если мы захотим изменить some_num в процедуре:

some_num = 3


Тогда будет ошибка. Почему? Несмотря на то, что переменная была передана через  ByRef , квалификатор Const гарантирует, что мы не будем изменять переданную переменную и компилятор зная это, конечно же высвечивает ошибку. Опять же ничто не мешает создать локальную копию этой константной переменной и работать с ней:

Dim As Integer copy_of_some_num = some_num
copy_of_some_num = 3


Теперь мы подошли к указателям. Как насчет них? Для указателей, это немного посложнее; Мы можем объявить сам указатель как Const, или то, на что он указывает , или даже то и другое! Поэтому допустимо следующее:

Declare Sub my_sub_a (ByRef ptr_A As Const Byte Ptr)

Declare Sub my_sub_b (ByRef ptr_B As Byte Const Ptr)

Declare Sub my_sub_c (ByRef ptr_C As Const Byte Const Ptr)


Первый из них  может изменить сам указатель, но не данные. Второй позволяет изменить данные, но не сможет изменить указатель на них. Третий не позволит вам изменить ни то, ни другое. Это великолепная защита против изменений! Поведение классификатора Const кажется немного странным, но он здорово поможет вам для безопасности исходных данных.  

Вы также можете использовать квалификатор Const в определяемых пользователем типах (UDT). Это немаловажная вещь в ООП , но даже если вы не используете ООП , по-прежнему можно использовать квалификаторы Const в ваших типах. Я не думаю , что очень необходимо показывать вам пример использования, ведь нового в принципе ничего нет, но на всякий случай:

Type my_type
  As Const Integer t_int= 5
End Type

Dim As my_type t

t.t_int = 3


Очевидно, что это не будет компилироваться, так как элемент t_int является Const. Кроме того, можно объявить переменную этого типа (в данном случае, t) с квалификатором Const. Ниже пример так же не будет компилироваться:

Type my_type
  As Integer t_int= 5
End Type

Dim As Const my_type t

t.t_int = 3


Что касается ООП  (и если вы не заинтересованы в ООП вы можете пропустить эту часть), то вам может быть интересно о методах. Посмотрите следующий пример:

Type my_object
  Public:
    Declare Sub modifier_sub ()
    
    'Процедуры-элементы, которые не изменяются объектом объявляются с Const...
    Declare Const Sub non_modifier_sub ()
  Private:
    some_num As Integer = 3
End Type

Sub my_object.modifier_sub ()
  this.some_num = 3
End Sub

Sub my_object.non_modifier_sub()
  Print this.some_num
End Sub

'Обратите внимание, что только Const объекты должны быть инициализированы , так же, как переменные.
'Таким образом вы должны иметь конструктор для объекта, или же вы должны дать всем переменным
'значения по умолчанию (как я сделал в этом примере), в этом случае компилятор сделает конструктор по умолчанию за вас.

Dim As Const my_object t = my_object
Dim As my_object u

'Оба из них не будут вызывать проблему при компиляции:
t.non_modifier_sub()
u.non_modifier_sub()

'...но первый из этих двух не позволит выполнить компиляцию, так как Const объекты не могут быть привлечены для не Const методов !
t.modifier_sub()
u.modifier_sub()

'останавливаемся для просмотра результатов
Sleep


Еще раз как это работает на основе простого правила. Неявно-переданные копии передаются через ByRef, любой метод обычно может изменить содержимое объекта , но если объект объявлен как Const, то этого не должно случиться! Таким образом есть по существу два вида метода. На странице документации C++ (перечислено ниже в разделе ссылки) для них определены имена: мутаторы(mutators) и инспекторы(inspectors). Мутаторы могут изменять объекты, но инспекторы нет. Таким образом для объектов, объявленных как Const, только инспектор-методы для этих объектов могут использоваться , хотя все методы могут быть вызваны для не Const объектов. Инспектор-методы это конечно же объявленные как Const методы. Таким образом для объектов Const могут использоваться только их Const методы.


Это все очень хорошо, но некоторые из вас могут задаться вопросом - зачем мне это нужно? Ну, такой же вопрос можно задать и по областям видимости... Конечно же это нужно для сокрытия определенных данных и в первую очередь для вашей собственной уверенности в том, что нужные вам данные не будут изменены тогда, когда вы меньше всего этого ожидаете. При использовании Const , компилятор будет вам помогать выявлять ошибки для тех областей, которые вы хотите видеть неизменными, но по забывчивости вы пытаетесь их изменить.


Некоторые окончательные примечания
Если вы используете квалификаторы Const, помните, что это относительно новая функция. Существует очень мало документации по этим новшествам, так что во многих случаях вам придется что-то понимать, экспериментируя. Если вы чувствуете, что что-то происходит не так, пожалуйста сообщите об этом на форуме! Хотя те вещи, которые я описал, должны работать именно так как описано. В любом случае , я советую вам использовать самую новую версию компилятора.

Если у вас есть какие-либо другие трудности с квалификаторами Const, помните, что, несмотря на то, что нет документации по этому вопросу, всегда есть много людей на форуме, которые помогут вам.

Если вы все еще не понимаете квалификаторы Const, вы, вероятно являетесь новичком, который еще очень плохо разбирается в областях видимости. Ничего страшного , все постепенно придет к вам , пока же вы можете изучить другие темы и писать программы без квалификаторов Const. Поверьте неиспользование данного новшества , не помешает вам писать прекрасные программы. А пока вы это делаете , документация скорее всего будет написана.

Наконец здесь приведены некоторые ссылки, которые должны быть полезны. Первая страница документации C++ о квалификаторах Const в C++ - конечно, это имеет смысл, только если вы понимаете, C++. Также есть ссылка на тему форума freebasic, в котором я спросил и узнал о квалификаторах Const в freebasic и ссылка на исходную страницу SourceForge - функцию запроса, в котором Const квалификаторы первоначально запрашивались:

http://www.parashift.com/c++-faq-lite/const-correctness.html
http://www.freebasic.net/forum/viewtopic.php?t=9975&postdays=0&postorder=asc&start=0
http://sourceforge.net/tracker/index.php?func=detail&aid=1480621&group_id=122342&atid=693199