Конструкторы и деструкторы
В числе методов, которые можно определить для структуры, есть специальные: конструктор(ы) и декструктор.
Конструктор имеет имя, совпадающее с именем структуры, и не возвращает значение (тип void). Конструктор, если он определен, будет вызываться в момент инициализации для каждого нового экземпляра структуры. За счет этого в конструкторе можно особым образом рассчитать начальное состояние структуры.
Структура может иметь несколько конструкторов, отличающихся набором параметров, и компилятор выберет соответствующий, руководствуясь числом и типом аргументов при определении переменной.
Например, мы можем описать пару конструктор в структуре Result: один — без параметров, и второй с одним параметром типа строка, для задания статуса.
struct Result
|
Кстати говоря, конструктор без параметров называется конструктором по умолчанию. Если явных конструкторов нет, компилятор неявным образом создает конструктор по умолчанию для любой структуры, в которой есть строки и динамические массивы, чтобы заполнить эти поля нулями.
Важно, что поля других типов (например, все числовые) не обнуляются, вне зависимости от того есть ли у структуры конструктор по умолчанию, а потому начальные значения элементов после выделения памяти будут случайными. Следует либо создать конструкторы, либо убедиться, что нужные значения присваиваются в коде сразу после создания объекта.
Наличие явных конструкторов делает невозможным использование синтаксиса агрегатной инициализации. В связи с этим строка Result r = {}; в методе calculate перестанет компилироваться. Теперь мы имеем право использовать только один из конструкторов, которые сами предоставили. Например, следующие инструкции вызывают конструктор без параметров:
Result r1;
|
А создание структуры с заполненным статусом можно сделать так:
Result r3("success"); |
Конструктор по умолчанию (явный или неявный) также вызывается при создании массива структур. Например, следующая инструкция выделяет память под 10 структур с результатами и инициализирует их с помощью конструктора по умолчанию:
Result array[10]; |
Деструктор — это функция, которая будет вызвана при уничтожении объекта структуры. Деструктор имеет имя, совпадающее с именем структуры, но с префиксом в виде символа тильды '~'. Деструктор также как и конструктор не возвращает значения, но он и не принимает параметров.
Деструктор может быть только один.
Явно вызвать деструктор нельзя. Программа сама делает это при выходе из блока кода, где была определена локальная переменная-структура, или при освобождении массива структур.
Назначение деструктора — освободить какие-либо динамические ресурсы, если структура их распределяла в конструкторе. Например, структура может обладать свойством персистентности, то есть сохранять свое состояние в файл при выгрузке из памяти и восстанавливать его, когда программа снова её создаст. При этом во встроенных файловых функциях используется дескриптор, который необходимо открывать и закрывать.
Определим деструктор в структуре Result и попутно дополним конструкторы, чтобы во всех этих методах велся учет количества экземпляров объектов (по мере того, как они создаются и уничтожаются).
struct Result
|
Три статических переменных с именем count существуют независимо друг от друга: каждая ведет подсчет в контексте своей функции.
В результате запуска скрипта мы получим следующий журнал:
Result::Result() 1
|
Разберемся, что это значит.
Первый экземпляр структуры создается в функции OnStart, в строке с вызовом calculate. При входе в конструктор значение счетчика count однократно инициализируется нулем и далее инкрементируется при каждом исполнении конструктора, поэтому в первый раз выводится значение 1.
Внутри функции calculate определяется локальная переменная типа Result, она зарегистрирована под номером 2.
Третий экземпляр структуры не столь очевиден. Дело в том, что для передачи результата из функции компилятор создает в неявном виде временную переменную, куда копирует данные локальной переменной. Вероятно, это поведение изменится в дальнейшем, и тогда локальный экземпляр будет "перемещаться" из функции без дублирования.
Последний вызов конструктора происходит в методе со строковым параметром, поэтому в нем счетчик вызовов равен 1.
Важно, что общее количество вызовов обоих конструкторов совпадает с количеством вызовов деструктора: 4.
Более подробно про конструкторы и деструкторы мы поговорим в Главе про классы.