Информатика и информационные технологии: конспект лекций
Шрифт:
Конструкторы и деструкторы являются специализированными формами методов. Используемые в связи с расширенным синтаксисом стандартных процедур New и Dispose конструкторы и деструкторы обладают способностью размещения и удаления динамических объектов. Кроме того, конструкторы имеют возможность выполнить требуемую инициализацию объектов, содержащих виртуальные методы. Как и все другие методы, конструкторы и деструкторы могут наследоваться, а объекты могут содержать любое число конструкторов и деструкторов.
Конструкторы используются для инициализации вновь созданных объектов. Обычно инициализация основывается на значениях, передаваемых конструктору в качестве параметров. Конструктор
Приведем несколько примеров конструкторов:
constructor Field.Copy(var F: Field);
begin
Self := F;
end;
constructor Field.Init(FX, FY, FLen: integer; FName: string);
begin
X := FX;
Y := FY;
GetMem(Name, Length (FName) + 1);
Name^ := FName;
end;
constructor TStrField.Init(FX, FY, FLen: integer; FName: string);
begin
inherited Init(FX, FY, FLen, FName);
Field.Init(FX, FY, FLen, FName);
GetMem(Value, Len);
Value^ := '';
end;
Главным действием конструктора порожденного (дочернего) типа, такого как указанный выше TStr Field. Init, почти всегда является вызов соответствующего конструктора его непосредственного родителя для инициализации наследуемых полей объекта. После выполнения этой процедуры конструктор инициализирует поля объекта, которые принадлежат только порожденному типу.
Деструкторы являются противоположностями конструкторов и используются для очистки объектов после их использования. Обычно очистка состоит в удалении всех полей указателей в объекте.
Примечание
Деструктор может быть виртуальным и часто является таковым. Деструктор редко имеет параметры.
Приведем несколько примеров деструкторов:
destructor Field.Done;
begin
FreeMem(Name, Length (Name^) + 1);
end;
destructor StrField.Done;
begin
FreeMem(Value, Len);
Field.Done;
end;
Деструктор дочернего типа, такой как указанный выше TStrField. Done, обычно сначала удаляет введенные в порожденном типе поля указателей, а затем в качестве последнего действия вызывает соответствующий сборщик-деструктор непосредственного родителя для удаления унаследованных полей указателей объекта.
3. Деструкторы
Borland Pascal предоставляет специальный тип метода, называемый сборщиком мусора (или деструктором) для очистки и удаления динамически размещенного объекта. Деструктор объединяет шаг удаления объекта с какими-либо другими действиями или задачами, необходимыми для данного типа объекта. Для единственного типа объекта можно определить несколько деструкторов.
Деструктор определяется совместно со всеми другими методами объекта в определении типа объекта:
tyрe
TEmployee = object
Name: string[25];
Title: string[25];
Rate: Real;
constructor Init(AName, ATitle: String; ARate: Real);
destructor Done; virtual;
function GetName: String;
function GetTitle: String;
function GetRate: Rate; virtual;
function GetPayAmount: Real; virtual;
end;
Деструкторы можно наследовать, и они могут быть либо статическими, либо виртуальными. Поскольку различные программы завершения, как правило, требуют различные типы объектов, обычно рекомендуется, чтобы деструкторы всегда были виртуальными, благодаря чему для каждого типа объекта будет выполнен правильный
Зарезервированное слово destructor не требуется указывать для каждого метода очистки, даже если определение типа объекта содержит виртуальные методы. Деструкторы в действительности работают только с динамически размещенными объектами.
При очистке динамически размещенного объекта деструктор осуществляет специальные функции: он гарантирует, что в динамически распределяемой области памяти всегда будет освобождаться правильное число байтов. Не может быть никаких опасений по поводу использования деструктора применительно к статически размещенным объектам; фактически, не передавая типа объекта деструктору, программист лишает объект данного типа полных преимуществ управления динамической памятью в Borland Pascal.
Деструкторы в действительности становятся самими собой тогда, когда должны очищаться полиморфические объекты и когда должна освобождаться занимаемая ими память.
Полиморфические объекты – это те объекты, которые были присвоены родительскому типу благодаря правилам совместимости расширенных типов Borland Pascal. Экземпляр объекта типа THourly присвоенный переменной типа TEmployee, является примером полиморфического объекта. Эти правила также могут быть применены к объектам; указатель на THourly может свободно быть присвоен указателю на TEmployee, а указуемый этим указателем объект опять же будет полиморфическим объектом. Термин «полиморфический» является подходящим, так как код, обрабатывающий объект, «не знает» точно во время компиляции, какой тип объекта ему придется в конце концов обработать. Единственное, что он знает, – это то, что этот объект принадлежит иерархии объектов, являющихся потомками указанного типа объекта.
Очевидно, что размеры типов объектов отличаются. Поэтому, когда наступает время очистки размещенного в динамической памяти полиморфического объекта, то как же Dispose узнает, сколько байт динамического пространства нужно освобождать? Во время компиляции из полиморфического объекта нельзя извлечь никакой информации относительно размера объекта.
Деструктор разрешает эту головоломку путем обращения к тому месту, где эта информация записана, – в ТВМ переменных реализаций. В каждой ТВМ типа объекта содержится размер в байтах данного типа объекта. Таблица виртуальных методов любого объекта доступна посредством скрытого параметра Self, посылаемого методу при вызове метода. Деструктор является всего лишь разновидностью метода, и поэтому, когда объект вызывает его, деструктор получает копию Self через стек. Таким образом, если объект является полиморфическим во время компиляции, он никогда не будет полиморфическим во время выполнения благодаря позднему связыванию.
Для выполнения этого освобождения памяти при позднем связывании деструктор нужно вызывать как часть расширенного синтаксиса процедуры Dispose:
Dispose(P, Done);
(Вызов деструктора вне процедуры Dispose вообще не выполняет никакого освобождения памяти.) Здесь происходит на самом деле то, что сборщик мусора объекта, на который указывает Р, выполняется как обычный метод. Однако, как только последнее действие выполнено, деструктор ищет размер реализации своего типа в ТВМ и пересылает размер процедуре Dispose. Процедура Dispose завершает процесс путем удаления правильного числа байт пространства динамической памяти, которое (пространство) до этого относилось к Р^. Число освобождаемых байт будет правильным независимо от того, указывал ли Р на экземпляр типа TSalaried, или он указывал на один из дочерних типов типа TSalaried, например на TCommissioned.