Конструкторы и деструкторы

Конструкторы и деструкторы отвечают за существование объекта в па­мяти, т.е. выделяют память для экземпляра класса, затем и освобождают ее.

Конструктор - это специальный вид подпрограммы, присоединенный к классу. Его назначение - создавать представителей (экземпляры) класса. Он ведет себя как функция, которая возвращает ссылку на вновь созданный эк­земпляр класса, т.е. на объект. Одновременно выделяется память для хране­ния значений полей экземпляра класса.

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

а) Синтаксис объявления конструкторов и деструкторов:

Type

<имя класса>=Сlass[{Имя родительского класса>)]

. . .

Constructor Имя конструктора>[(<параметры>)];[Override;]

Destructor <имя деструктора>[(<параметры>)};[Override;] End;

Примечания:

Объявляются конструкторы и деструкторы, как правило, в разделе Public класса.

В классе может быть объявлено несколько конструкторов, однако чаще бывает один конструктор. Общепринятое имя для единственного кон­структораCreate.

• В одном классе может быть объявлено несколько деструкторов, но чаще бывает один деструктор без параметров (всегда!) с именемDestroy.

За объявлением деструктора по имени Destroy следует указывать ключевое слово-директивуOverride, разрешающее выполнение предусмот­ренных по умолчанию действий для уничтожения экземпляра объекта, если при его создании возникла какая-либо ошибка. Фактически Override переоп­ределяет метод предка - новая начинка и старое имя.

• МетодFree так же удаляет (разрушает) экземпляры класса, предва­рительно проверяя их на Nil.

Type TPictureShow=Class Public

FFilm: TFilm;

FTitle: String;

Constructor Create(AnTitle: String);

Destructor Destroy; Override;

Function GetFilm: TFilm;

Procedure SetFilm(Const AnFilm: TFilm);

Function GetTitIe: String;

Procedure SetTitle(AnTitle: String);

End;

б) Реализация конструкторов.

В задачу конструктора входит создание экземпляра класса и выполнение операторов, содержащихся в его теле. Заботиться о деталях того, как именно при работе программы в памяти будет создан новый экземпляр класса, нет необходимости. К моменту выполнения операторов конструктора он будет создан автоматически и назначение кода внутри конструктора - инициализи­ровать только что созданный экземпляр объекта. Синтаксис реализации кон­структора:

Constructor <имя класса>.<имя конструктора>[(<параметры>}};

[<блок объявлений>}

Begin

<Исполняемые операторы>

End;

в) Реализация наследуемых конструкторов.

Constructor <имя класса>.<имя конструктора>[(<параметры>)];

[<блок обьявлений>}

Begin

Inherited <имя конструктора>[(<параметры>)};

<инициализация собственных полей>

End;

Constructor TPictureShow.Create(AnTitle: String);

Begin

Inherited Create;

FTitle:=AnTitle;

FFilm:=TFilm.Create; // FFilm: =Nil; если объект пока не нужен End;

Примечания:

Реализация метода начинается с указания зарезервированного слова Procedure\Function, за которым следует полное имя метода и параметры:

<имя класса>.<имя метода>[(<параметры>)];

Для метода-функции следует указать и <тип результатам.

Следует убедиться, что для каждого поля в конструкторе преду­смотрен оператор присвоения, и что все поля переходят из неопределенного состояния в какое-то конкретное, предусмотренное по умолчанию, хотя и известно, что транслятор сам выполнит инициализацию нулевыми значе­ниями ('', Nil, False - для других полей). Для неклассовых полей такая инициа­лизация вполне допустима, поскольку позволяет их использовать в дальней­шем. Инициализация классового поля значением Nil не позволяет непосред­ственное использование такого поля, поскольку память для хранения данных не отведена. При попытке обращения к такому полю будет выведено сооб­щение примерно следующего вида:

Для вызова наследуемого конструктора следует использовать клю­чевое словоInherited, которое фактически обеспечивает доступность пере­крытого метода. Сила оператора в том, что он вызывает старое, а затем возвращается к новому.

Как правило, следует вызывать подходящий наследуемый конструк­тор в первом исполняемом операторе.

Только, если у пользовательского класса нет новых полей, можно не создавать для него конструктор.

В тех случаях, когда по каким-либо причинам не удается инициализи­ровать экземпляр класса полностью, должен быть предусмотрен механизм прекращения создания объекта и возврата к коду, вызвавшему конструктор, проинформировав его об ошибке и установив ссылку на объект в Nil. Ранее для этих целей была предусмотрена предопределенная процедураFail. Она может использоваться сейчас, но только внутри конструкторов. Однако более прогрессивным считается использование механизма исключений, но о нем позже.

• Хотя в объявлениях конструкторов и не указывается тип возвращае­мого результата, и они выглядят, как объявления процедур, конструкторы используются скорее как функции, а не как процедуры. Можно сказать, что конструктор является неявной функцией - он возвращает нового предста­вителя того класса, который использовался при его вызове. Нет необходи­мости в явном виде задавать тип возвращаемого результата, поскольку этот тип и так известен на момент вызова - это тип использованного в вызове конструктора класса.

Внутри конструктора отсутствует и явное присвоение возвращае­мого значения. Такое возможно, поскольку он всегда возвращает ссылку (адрес) на вновь созданный и инициализированный экземпляр класса.

Начиная с класса TComponent конструктор Create стал виртуальным и при его переопределении необходимо указывать слово-директиву Override, назначение которого будет пояснено позднее.

т) Реализация деструкторов.

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

Как и в случае с конструктором нет необходимости заботиться о деталях освобождения динамической памяти уничтожаемого объекта, поскольку это делается автоматически. Программист ответственен за вызов деструкторов для всех экземпляров класса, если были зарезервированы подчиненные объ­екты.

Синтаксис реализации деструктора:

Destructor <имя класса>.<имя деструктора>[(<параметры>)];

[<блок объявлений>]

Begin

<исполняемые операторы>

End;

Реализация наследуемых деструкторов.

Если использовать механизм наследования деструкторов, то можно уп­ростить задачу уничтожения экземпляров класса, таким образом, чтобы каж­дый раз заботиться лишь об уничтожении тех полей, которые были добавле­ны в данном классе. Всю работу по очистке наследуемых полей можно воз­ложить на наследуемые деструкторы. Для вызова наследуемого деструктора необходимо используется ключевое словоInherited. Синтаксис объявления наследуемого деструктора следующий:

Destructor <имя класса>.<имя деструктора>[(<параметры>)};

[<блок объявлений>]

Begin

<уничтожение собственных полей>

Inherited <имя деструктора>[{<параметры>)];

End;