Borland Pascal. Руководство пользователя

       

Деструкторы


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, а указуемый этим указателем объект опять же будет полиморфическим объектом.


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

Очевидно, что размеры типов объектов отличаются. Поэтому, когда наступает время очистки размещенного в динамической памяти полиморфического объекта, то как же Disрose узнает, сколько байт динамического пространства нужно освобождать? Во время компиляции из полиморфического объекта нельзя извлечь никакой информации от- носительно размера объекта.

Деструктор разрешает эту головоломку путем обращения к тому месту, где эта информация записана: в ТВМ переменных реализаций. В каждой ТВМ типа объекта содержится размер в байтах данного типа объекта. Таблица виртуальных методов любого объекта доступна пос- редством скрытого параметра Self, посылаемого методу при вызове метода. Деструктор является всего лишь разновидностью метода и поэтому, когда объект вызывает его, деструктор получает копию Self через стек. Таким образом, если объект является полиморфи- ческим во время компиляции, он никогда не будет полиморфическим во время выполнения благодаря позднему связыванию.

Для выполнения этого освобождения памяти при позднем связы- вании деструктор нужно вызывать, как часть расширенного синтакси- са процедуры Disрose:

Disрose(P, Done);

(Вызов деструктора вне процедуры Disрose вообще не выполняет никакого освобождения памяти.) Здесь происходит на самом деле то, что сборщик мусора объекта, на который указывает P, выполняется как обычный метод. Однако, как только последнее действие выполне- но, деструктор ищет размер реализации своего типа в ТВМ и пересы- лает размер процедуре Disрose. Процедура Disрose завершает про- цесс путем удаления правильного числа байт пространства динами- ческой памяти, которое (пространство) до этого относилось к P^. Число освобождаемых байт будет правильным независимо от того, указывал ли P на экземпляр типа TSalaried, или он указывал на один из дочерних типов типа TSalaried, например, на TCommissioned.

Заметьте, что сам по себе метод деструктора может быть пуст и выполнять только эту функцию:

destructor AnObject.Done; begin end;

То, что делается полезного в этом деструкторе, не является достоянием его тела, однако при этом компилятором генерируется код эпилога в ответ на зарезервированное слово destructor. Это напоминает модуль, который ничего не экспортирует, но который осуществляет некоторые невидимые действия за счет выполнения сво- ей секции инициализации перед стартом программы. Все действия происходят "за кулисами".


Содержание раздела