Конструкторы и деструкторы
Конструкторы и деструкторы отвечают за существование объекта в памяти, т.е. выделяют память для экземпляра класса, затем и освобождают ее.
Конструктор - это специальный вид подпрограммы, присоединенный к классу. Его назначение - создавать представителей (экземпляры) класса. Он ведет себя как функция, которая возвращает ссылку на вновь созданный экземпляр класса, т.е. на объект. Одновременно выделяется память для хранения значений полей экземпляра класса.
Деструктор - это специальная разновидность подпрограммы, присоединенной к классу. Его назначение заключается в уничтожении экземпляра класса, т.е. объекта и освобождении памяти, выделенной под экземпляр.
а) Синтаксис объявления конструкторов и деструкторов:
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;