«Очень важно не прерывать вопросов. Любопытство имеет свое право на существование»
26. Конструкторы и деструкторы
Конструкторы (Constructors).
Конструктор – это специальный метод, который создает и инициализирует объект. Объявление конструктора выглядит как объявление процедуры, однако начинается со слова constructor. Примеры:
constructor Create;
constructor Create(AOwner: TComponent);
Конструкторы должны использовать принятое по умолчанию соглашение по вызовам register. Хотя объявление конструктора не содержит возвращаемого значения, при его вызове с указанием типа класса он возвращает ссылку на созданный им объект, например
MyObject := TMyClass.Create;
Этот вызов выделяет для нового объекта динамическую память, инициализирует все поля порядкового типа нулями, присваивает nil всем указателям и полям типа class и делает все поля строкового типа пустыми. Далее выполняются другие операторы конструктора. Обычно, объект инициализируется на базе значений, которые передаются конструктору посредством фактических параметров. Наконец, конструктор возвращает ссылку на вновь созданный объект. Тип возвращаемого значения такой же, как и тип класса, который был указан при вызове конструктора.
Если при работе конструктора, вызванного с указанием имени класса, возникает исключительная ситуация, автоматически вызывается деструктор Destroy и уничтожает незавершенный объект.
Когда конструктор вызывается с указанием имени объекта (в противоположность указанию имени класса), он не создает объект и не возвращает значения. Вместо этого конструктор выполняет только те операторы, которые в нем явно указаны.
Пример описания класса и конструктора:
type
TShape = class(TGraphicControl)
private
FPen: TPen;
FBrush: TBrush;
procedure PenChanged(Sender: TObject);
procedure BrushChanged(Sender: TObject);
public
constructor Create(Owner: TComponent); override;
destructor Destroy; override;
…
end;
constructor TShape.Create(Owner: TComponent);
begin
inherited Create(Owner); // инициализация унаследованных частей
Width := 65; // Изменение унаследованных свойств
Height := 65;
FPen := TPen.Create; // инициализация собственных (новых) полей
FPen.OnChange := PenChanged;
FBrush := TBrush.Create;
FBrush.OnChange := BrushChanged;
end;
Первым делом, обычно, конструктор вызывает унаследованный конструктор. Затем инициализируются новые поля.
Для ответа на вопрос о том, каким должен быть конструктор – статическим, виртуальным или перекрытым – вернемся к программе Method_Binding, в которой рассматривалась такая иерархия классов:
type
TFigure = class
// объявления конструктора, деструктора, полей и методов
end;
TRectangle = class(TFigure)
// объявления конструктора, деструктора, полей и методов
end;
TEllipse = class(TRectangle)
// объявления конструктора, деструктора, полей и методов
end;
Попробуйте теперь ответить на вопрос, как должны быть описаны конструкторы классов, чтобы можно было создавать любые объекты иерархии с использованием переменной типа TFigure:
var F : TFigure;
Объект какого типа должен создаваться при выполнении оператора F:=TRectangle.Create? Типа TFigure или типа TRectangle?
Наиболее ярко особенности виртуальных конструкторов проявляются с типами ссылок на класс, когда конструируются объекты, тип которых на этапе компиляции неизвестен. Они будут рассмотрены позже (см. раздел «Ссылки на класс»). В остальных случаях виртуальные конструкторы ведут себя так же, как и статические.
Деструкторы (Destructors).
Это специальные методы, которые разрушают объекты и освобождают занимаемую ими память. Объявление деструктора подобно объявлению процедуры, но вместо слова процедура указывается destructor, например
destructor Destroy;
destructor Destroy; override;
Как и конструкторы, деструкторы должны использовать соглашение по вызовам register. Хотя класс может иметь более чем один деструктор, рекомендуется перекрывать унаследованный деструктор и не объявлять других деструкторов.
Для вызова деструктора надо указать имя объекта, например:
MyObject.Destroy;
При вызове деструктора сначала выполняются операторы, указанные в его реализации. Обычно это уничтожение внедренных объектов и освобождение ресурсов, выделенных объекту. Затем освобождается память, выделенная объекту. Последним, обычно, вызывается унаследованный деструктор для уничтожения унаследованных полей. Вот пример деструктора:
destructor TShape.Destroy;
begin
FBrush.Free;
FPen.Free;
inherited Destroy;
end;
При возникновении исключительной ситуации на этапе создания объекта конструктором вызывается деструктор для уничтожения "недостроенного" объекта. Следовательно, реализация деструктора должна предусматривать разрушение частично построенного объекта. Так как конструктор инициализирует поля указатели и ссылки на класс значениями nil, деструктор должен проверить эти значения перед выполнением операций над такими полями. По этой причине безопаснее вызывать метод Free(определенный уже в TObject) вместо метода Destroy, так как метод Free выполняет проверку значения указателя на объект (отличается ли его значение от nil) перед освобождением памяти. Вот реализация метода Free в модуле System:
procedure TObject.Free;
begin
if Self <> nil then Destroy;
end;
Замечание. Действительно, метод Free можно вызывать для объекта, имеющего значение nil, в то же время вызов любого другого метода, в том числе и деструктора, приводит к ошибке. |
«25. Связывание методов»
27. Введение в обработку сообщений