Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

03 Лекция - Реализация классов

.pdf
Скачиваний:
41
Добавлен:
13.04.2015
Размер:
611.81 Кб
Скачать

Лекция № 3.

 

Тема: Реализация классов.

 

1.

Понятие класса.......................................................................................................................................

1

2.

Дополнительные сведения о методах..................................................................................................

2

3.

Инкапсуляция.........................................................................................................................................

3

4.

Спецификаторы доступа Private, Protected и Public...........................................................

3

5.

Инкапсуляция со свойствами ...............................................................................................................

4

6.

Конструкторы ........................................................................................................................................

5

7.

Деструкторы и метод Free...................................................................................................................

6

8.

Модель объектных ссылок Delphi .....................................................................................................

6

9.

Присвоение объектов ............................................................................................................................

7

10. Объекты и память ................................................................................................................................

8

1. Понятие класса

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

В ООП описания объектов называются классами (class).

Класс фундаментальное понятие DELPHI, он лежит в основе многих свойств DELPHI. Класс предоставляет механизм для создания объектов. В классе отражены важнейшие концепции объектноориентированного программирования: инкапсуляция, наследование, полиморфизм.

С точки зрения синтаксиса, класс в DELPHI это структурированный тип, образованный на основе уже существующих типов. В этом смысле класс является расширением понятия записи. Но если запись имела только свойства (поля) и ничего более, то классы могут содержать все, что требуется для полноценного объекта. При этом класс может быть сколь угодно большим и иметь в качестве своих свойств другие классы. Например, класс "машина" может иметь в своем описании такие свойства, как двигатель, шасси и им подобные, представленные другими классами. При этом для каждого такого класса так же могут быть определены собственные свойства и методы.

Функции – это методы класса, определяющие операции над объектом.

Данные – это поля объекта, образующие его структуру. Значения полей определяет состояние объ-

екта.

Пример.

Type

 

 

TPoint = class // дата

 

fX, fY: Real; // поля: координаты точки

 

procedure setX(const x: Real); // метод – установить координату X

 

procedure setY(const y: Real); // метод – установить координату Y

 

function GetX: real; function GetY: real;

// метод – по-

лучить координаты

 

 

procedure print;

// метод – вывести точку

 

function Distance( p: TPoint): real;//метод – получить расстояние до другой точки end;

Обратим внимание на имя класса — TPoint. Оно начинается с буквы T. Это один из элементов, так называемой венгерской нотации — соглашения об именах. T означает, что это имя типа. Fx означает, что это поле (field).

Приведенное объявление демонстрирует одну из сторон понятия инкапсуляции — элементы данных объединены в одно понятие со средствами работы с этими данными. Описания алгоритмов методов класса должны располагаться в части implementation того же модуля, где объявлен класс и выглядят следующим образом:

procedure TPoint.SetX(const x: real) ; begin

Fx := x; end;

procedure TPoint.SetY(const y: real); begin

Fy := y; end;

function TPoint.GetX: real; begin

GetX := Fx; end;

function TPoint.GetY: real; begin

GetY := Fy; end;

function TPoint.Distance( p: TPoint): real; begin

Distance := sqrt(sqr(Fx - p.Fx) + sqr(Fy - p.Fy)) end;

Самими же объектами в программе называют не классы, а их экземпляры. Т.е. если мы имеем класс TPoint, то его экземпляр, представленный в виде переменной типа TPoint, как раз и будет объектом.

Для описания объекта класса (экземпляра класса) используется конструкция

Var имя_объекта: имя_класса;

var

 

Point1, my_point: TPoint;

 

Points: array[1..30] of TPoint;

// массив объектов

В определяемые объекты входят данные, соответствующие полям класса. Методы класса позволяют обрабатывать данные конкретных объектов класса. Обращаться к данным объекта и вызывать функции для объекта можно с помощью “квалифицированных” имен:

имя_объекта. имя_данного имя_объекта. имя_функции

Например: my_point.x:=19; my_point.y:=7; Point1.set(31,12); Points[1].print;

2.Дополнительные сведения о методах

Ометодах можно говорить очень много. Вот некоторые небольшие примечания о возможностях, доступных в Delphi:

• Delphi поддерживает перегрузку (overloading) метода. Это означает, что допускается существование двух методов с одинаковым именем, помеченных ключевым словом overload и тем, что списки параметров этих методов полностью отличаются. Выбор нужного метода осуществляется компилятором путем проверки параметров;

• методы могут иметь один или несколько параметров со значениями по умолчанию. Если при вызове эти параметры упущены, то им будут присвоены значения по умолчанию;

• внутри метода можно использовать ключевое слово Self для обращения к текущему объекту. При обращении к локальным данным объекта ссылка на self является неявной. Например, в методе SetX класса

TPoint, рассматриваемого выше, для обращения к полю текущего объекта можно использовать FX, а компилятор транслирует ее в Self.FX;

можно определять методы класса с пометкой ключевым словом class. Метод класса не имеет экземпляр объекта для действия, поскольку он может применяться к объекту класса или к классу целиком;

по умолчанию методы используют регистровое соглашение вызова register: параметры и возвращаемые значения передаются из вызывающего кода к функции и обратно с помощью регистров центрального процессора, а не через стек, что значительно ускоряет вызовы методов.

3. Инкапсуляция

Класс может содержать большое количество данных и любое число методов. Однако для соблюдения объектно-ориентированного подхода данные должны быть скрыты или инкапсулированы внутри использующего их класса. При обращении к данным само по себе изменение их значения, например, даты, не имеет большого значения. Фактически же значение дня может оказаться недействительным, например, 30 февраля. Использование для обращения к внутреннему представлению объекта методов ограничивает риск возникновения ошибочной ситуации, поскольку эти методы могут проверять действительность нового значения и отклонять его. Инкапсуляция важна потому, что она позволяет создателю класса изменять в последующих версиях внутреннее представление.

Концепция инкапсуляции зачастую обозначается «черным ящиком». Нет необходимости беспокоиться о его содержимом: необходимо лишь знать, как осуществлять взаимодействие с этим черным ящиком, т. е. использовать его, не вникая во внутреннюю структуру. Раздел «как им пользоваться» - называется интерфейсом класса. Он позволяет другим частям программы обращаться к объектам этого класса и использовать их. Однако при использовании объектов большая часть кода остается скрытой. Редко удается узнать, какими внутренними данными оперирует объект, и, как правило, отсутствует возможность непосредственного доступа к этим данным. Конечно же, предполагается, что для обращения к этим данным используются методы, защищающие от несанкционированного доступа. Это является объектноориентированным подходом к классической концепции программирования, известной как сокрытие информации (information hiding). Однако, в Delphi существует и дополнительный уровень сокрытия посредством свойств.

Delphi осуществляет инкапсуляцию, основанную на классах, но используя структуру модулей, все еще поддерживает классическую инкапсуляцию, основанную на модулях. Каждый идентификатор, который объявлен в разделе interface (интерфейс) модуля, становится видимым в других модулях программы, что обеспечивается использованием выражения uses, в котором помещается ссылка на модуль, в котором объявлен идентификатор. А идентификаторы, объявленные в разделе implementation (реализации) модуля, являются локальными для этого модуля.

4. Спецификаторы доступа Private, Protected и Public

Для инкапсуляции, основанной на классах, язык Delphi предлагает три спецификатора доступа: private (частный), protected (защищенный) и public (общий). Четвертый, published (опубликованный), управляет RTTI-информацией (run-time type information) и информацией периода разработки (design-time information), но он дает ту же программную доступность, как и спецификатор public. Вот описание трех классических спецификаторов доступа:

директива private представляет поля и методы класса, которые недоступны извне модуля, объявляющего этот класс;

директива protected используется для указания методов и полей с ограниченной «видимостью». К элементам, помеченным как protected, могут обращаться только текущий класс и его наследники. Точнее, только этот класс, его подклассы и любой программный код, расположенный в том же модуле, что и данный класс, могут обращаться к защищенным элементам, которые открываются с помощью «обходного маневра», рассмотренного во вставке «Доступ к защищенным данным других классов» далее в этой главе. Это ключевое слово мы также рассмотрим в разделе «Защищенные поля и инкапсуляция»;

директива public представляет поля и методы, которые свободно доступны из любого раздела программы, а также из модуля, в котором они определены.

В общем случае поля класса должны быть объявлены как private, а методы — как public. Однако это не всегда имеет место. Методы могут быть частными или защищенными, если они нужны только для внутреннего применения в целях выполнения каких-либо частичных вычислений или для реализации свойств. Поля могут быть объявлены как защищенные, для того чтобы ими можно было управлять в клас- сах-наследниках, хотя это не считается хорошим стилем ООП.

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

В качестве примера рассмотрим класс TDate: type

TDate = class private

Month, Day, Year: Integer; public

procedure SetValue (y, m, d: Integer); overload; procedure SetValue (NewDate: TDateTime); overload; function LeapYear: Boolean;

function GetText: string; end;

Возможно, вам захочется добавить и другие функции, такие как GetOay, GetMonth и GetYear, которые бы возвращали соответствующие защищенные данные, но на самом деле подобные функции непосредственного доступа к данным совершенно не нужны. Предоставляя функции доступа для всех и каждого, поля приводят к снижению возможностей инкапсуляции и затрудняют модификацию внутренней реализации класса. Функции доступа должны предоставляться лишь в том случае, если они являются частью логического интерфейса реализуемого вами класса.

Теперь заменим определение класса на следующее. type

TDate = class private

fDate: TDateTime; public

procedure SetValue (y, m, d: Integer); overload; procedure SetValue (NewDate: TDateTime); overload; function LeapYear: Boolean;

function GetText: string; end;

Обратите внимание, что ввиду изменения только в разделе private данного класса, нет необходимости модифицировать все использующие его программы. Вот в чем преимущество инкапсуляции!

5. Инкапсуляция со свойствами

Свойства являются довольно значимым механизмом ООП или хорошо обоснованным приложением идеи инкапсуляции. По существу, вы имеете имя, которое полностью скрывает детали выполнения. Это позволяет значительно изменять класс, не воздействуя на использующий его код. Удачное определение свойств звучит как виртуальные поля. С точки зрения пользователя класса, свойства выглядят именно как поля, поскольку их значения обычно можно прочитать или записать. Например, этим программным кодом можно прочитать значение свойства Caption кнопки и назначать его свойству Text строки редактирования:

Editl.Text := Buttonl.Caption;

Это выглядит как операции чтения поля и записи в поле. Однако для чтения и записи значения свойства могут непосредственно отображаться на данные и на методы доступа. Когда свойства отображаются на методы, то данные, к которым они обращаются, могут быть частью объекта или быть за его пределами, что может вызвать побочные эффекты, например, перерисовку элемента управления после изменения одного из его значений. Технически свойство — это идентификатор, который отображен на данные или методы с использованием инструкции read или write. Например, определение свойства Month для класса дат выглядит следующим образом:

property Month: Integer read FMonth write SetMonth;

Для обращения к значению свойства Month программа производит чтение защищенного поля FMonth; для изменения значения свойства — вызывает метод SetMonth (который, конечно же, должен быть объявлен внутри класса).

Допускаются различные комбинации (например, для чтения можно использовать метод, а для изменения поля — непосредственно директиву write), но обычно для изменения значения свойства используется метод. Вот два альтернативных определения свойства, отображенные на два метода доступа или отображенные непосредственно на данные в обоих направлениях:

property Month: Integer read GetMonth write SetMonth; property Month: Integer

read FMonth write FMonth;

Зачастую фактические данные и методы доступа являются частными (private) (или защищенными (protected)), в то время как свойство — опубликованным (public). По этой причине для доступа к этим методам или данным необходимо использовать свойство: эта технология обеспечивает как расширенную, так и упрощенную версию инкапсуляции. Расширенную, поскольку имеется возможность изменять не только представление данных и функции доступа к ним, но и добавлять или удалять функции доступа без изменения вызывающего программного кода. Пользователь лишь должен повторно скомпилировать программу, использующую это свойство.

В качестве примера добавим в объект рассматриваемого ранее класса TDate свойства доступа к году, месяцу и дню. Эти свойства не отображаются на определенные поля, но все они отображаются на единое поле fDate, хранящее полные сведения о дате. Вот почему все свойства имеют методы-«получатели» и методы-«установщики»:

type

TDate = class public

property Year: Integer read GetYear write SetYear; property Month: Integer read GetMonth write SetMonth; property Day: Integer read GetDay write SetDay;

Каждый из этих методов легко реализуется с помощью функций, доступных в модуле DateUtils. Ниже представлен программный код двух из них (остальные очень просты):

function TDate.GetYear: Integer; begin

Result := YearOf (fDate); end;

procedure TDate.SetYear(const Value: Integer); begin

fDate := RecodeYear (fDate, Value); end;

Свойства имеют ряд расширенных возможностей. Вот краткая сводка наиболее важных характери-

стик:

директива write свойства может быть опущена, что сделает его свойством «только для чтения». При попытке изменить свойства компилятор выдаст ошибку. Также можно опустить директиву read и определить таким образом свойство «только для записи», но такой подход лишен смысла и используется редко;

IDE среды Delphi дает специальную трактовку свойствам этапа разработки, которые объявлены с помощью спецификатора доступа published и для выбранного компонента представлены в инспекторе объ-

ектов (Object Inspector);

альтернативным способом является объявление свойств с помощью спецификатора public (такие свойства зачастую называются «свойство времени выполнениям). Эти свойства могут использоваться в программном коде;

можно определить свойства на основе массива, которые для обращения к элементу списка используют характерную нотацию в квадратных скобках. Свойства на основе списка строк, такие как Lines, являются типичным примером этой группы;

свойства имеют специальные директивы (включая stored и default), которые управляют поточной системой передачи компонентов.

Обычно имеется возможность присвоить значение свойству или прочитать его и даже использовать войства в выражениях, но далеко не всегда можно передавать свойство в процедуру или метод в качестве параметра. Дело в том, что свойство — это не указатель на определенное место в памяти, поэтому оно не может использоваться в качестве параметра var или out; оно не может передаваться посредством ссылки.

6. Конструкторы

Для выделения памяти для объектов в Delphi обычно вызывается метод Create. Это — конструктор, т. е специальный метод, применяемый к классу для выделения памяти для размещения экземпляра этого класса. Экземпляр возвращается конструктором, и для того чтобы сохранить этот объект и использовать это позже, может быть присвоен переменной. Все данные нового экземпляра установлены в ноль. Если необходимо, чтобы данные экземпляра имели определенные значения, то вы должны написать собственный конструктор.

Перед конструктором ставится ключевое слово constructor. Хотя можно задать любое имя, лучше придерживаться стандартного имени: Create. Если используется другое имя, то конструктор Create базового класса TObject так и останется доступным, а программист, вызвавший этот стандартный конструктор, может пропустить созданный вами код инициализации.

За счет определения конструктора Create с некоторыми параметрами, вы заменяете заданное по умолчанию определение новым и делаете его использование обязательным. Например, после того как будет определено:

type TDate = class public constructor Create (y, m, d: Integer);

Можно будет вызвать только этот конструктор, а не стандартный:

var

ADay: TDate; begin

//ошибка, не компилируется:

ADay := TDate.Create;

//правильно:

ADay := TDate.Create (1, 1, 2000);

Правила по написанию конструкторов для собственных компонентов различны. Причина заключается в том, что в этом случае необходимо подменить (override) виртуальный конструктор. Подмена особенно уместна для конструкторов, поскольку в класс можно добавлять множество конструкторов и вызывать их всех по имени Create; этот подход делает конструкторы простыми для запоминания и является стандартным путем, поддерживаемым другими ООП-языками, в которых все конструкторы должны иметь одинаковое имя. В качестве примера добавим в класс два отдельных конструктора Create; один из них — без параметров. Он скрывает заданный по умолчанию конструктор. А другой — со значениями инициализации. Конструктор без параметра использует сегодняшнюю дату как заданное по умолчанию значение:

type

TDate = class public constructor Create; overload;

constructor Create (y, m, d: Integer); overload;

7. Деструкторы и метод Free

Точно так же как класс может иметь пользовательский (измененный) конструктор, он может иметь пользовательский деструктор — метод, объявленный с ключевым словом destructor, и вызываемый по имени Destroy. Так же, как вызов конструктора выделяет память для объекта, вызов деструктора освобождает память. Деструкторы нужны только для объектов, которые запрашивают внешние ресурсы у своих конструкторов или во время существования. Можно написать новый деструктор, полностью подменяющий стандартный деструктор Destroy, позволив объекту выполнить некоторую очистку прежде, чем он сам будет разрушен.

Destroy — это виртуальный деструктор класса TObject. Нельзя определять другой деструктор, потому что объекты обычно уничтожаются вызовом метода Free, а этот метод вызывает виртуальный деструктор Destroy определенного класса (виртуальный метод будет обсужден далее в этой главе).

Free — это метод класса TObject, наследуемый всеми другими классами. Метод Free перед вызовом виртуального деструктора Destroy обычно проверяет, не является ли текущий объект (Self) нулевым (nil). Метод Free не сбрасывает объект в ноль автоматически; это то, что вы должны сделать сами. Объект «не знает», какие переменные могут ссылаться на него, поэтому не имеет возможности сделать их все нулевыми.

ВDelphi имеется процедура FreeAndNil, которую можно использовать сразу для освобождения объекта и сброса его ссылки в ноль. Вызывайте метод FreeAndNil(Obj) вместо написания

Obj.Free; Obj := nil;

8.Модель объектных ссылок Delphi

Внекоторых ООП-языках объявление переменной типа класса создает экземпляр этого класса. Вместо этого Delphi основан на модели объектных ссылок (object reference model). Основная идея заключается в том, что переменная, имеющая тип-класс (например, переменная TheDay в рассмотренном ранее примере ViewDate), не содержит этот объект в качестве значения. Она лишь содержит указатель (pointer) или ссылку, указывающую, в каком месте памяти расположен объект (рис.1).

Рис.1. Представление структуры объекта в памяти и переменная, ссылающаяся на него

Единственная проблема этого подхода состоит в том, что при объявлении переменной вы не создаете объект в памяти (который несовместим со всеми другими переменными, что сбивает новых пользователей Delphi); вы лишь резервируете место в памяти для ссылки на объект. Экземпляры объекта должны быть созданы вручную, по крайней мере для определенных вами объектов классов. Экземпляры компонентов, помещаемых на форме, строятся автоматически библиотекой Delphi.

Вы видели, как создать экземпляр объекта, применив конструктор к «его» классу. После того как объект был создан и необходимость в нем отпала, требуется удалить его (чтобы избежать потери уже «ненужной» памяти, что ведет к так называемой «утечке памяти»). Освобождение памяти осуществляется методом Free. Если вы создаете объекты по мере необходимости и освобождаете их по завершении работы с ними, модель объектных ссылок работает без сбоя. Как вы увидите далее, использование модели объектных ссылок имеет далеко идущие последствия.

9. Присвоение объектов

Если переменная, содержащая объект, лишь содержит ссылку на этот объект, размещенный в памяти, то что произойдет в случае копирования значения этой переменной? Предположим, что вы записали некоторый метод следующим образом;

procedure TDateForm.BtnTodayClick(Sender: TObject); var

NewDay: TDate; begin NewDay := TDate.Create; TheDay := NewDay;

LabelDate.Caption := TheDay.GetText; end;

Этот программный код копирует адрес памяти объекта NewDay в переменную TheDay (рис. 2); это не приводит к копированию данных одного объекта в другой.

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

NewDay TDate object

TheDay

Рис. 2. Представление операции присваивания объектной ссылки на другой объект в отличие от копирования содержимого объекта в другой объект

Эта специфичная проблема может быть разрешена путем освобождения старого объекта нижеприведенным программным кодом (который лишь упрощен: он не использует яв-

ной переменной для только что созданного объекта):

procedure TDateForm.BtnTodayClick(Sender: TObject); begin

TheDay.Free;

TheDay := TDate.Create;

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

var

ADay: TDate; begin

ADay := Userlnformation.GetBirthDate;// использование ADay

To же самое происходит при передаче объекта в функцию в качестве параметра: вы не создаете новый объект, а ссылаетесь на один и тот же объект в двух различных местах программного кода. Например, при написании представленной ниже процедуры ее вызов приводит к изменению свойства Caption объекта Button1, а не к копированию ее данных в память (которая совершенно бесполезна):

procedure CaptionPlus (Button: TButton); begin

Button.Caption := Button.Caption + '+'; end;

// вызов

CaptionPlus (Button1)

Это означает, что объект передается ссылкой без использования ключевого слова var и без любого другого очевидного семантического признака «передачи-по-ссылке», что также смущает новичков. Что случится, если вы действительно хотите изменить данные внутри существующего объекта таким образом, чтобы они соответствовали данным другого объекта? Необходимо копировать каждое поле объекта, что возможно, только если все они объявлены как public, или предоставить специальный метод для копирования внутренних данных.

10. Объекты и память

Управление памятью в Delphi подчиняется трем правилам, по меньшей мере, если вы позволяете среде работать гармонично, без нарушений доступа (Access Violations) и вхолостую расходуя лишнюю память:

перед использованием любого объекта его необходимо создать;

после использования любого объекта его необходимо уничтожить;

каждый объект должен уничтожаться только один раз.

Должны ли вы руководствоваться этими инструкциями или можете позволить заниматься этим среде Delphi, зависит от выбранного вами подхода.

Для динамических элементов Delphi поддерживает три типа управления памятью:

при явном создании объекта в программном коде приложения вы обязаны освободить его (с единственным исключением ряда системных объектов и объектов, которые используются посредством интерфейсных ссылок). Если этого не делать, то до завершения программы память, используемая этим объектом, не будет освобождена для других объектов;

при создании компонента вы можете указать владельца этого компонента, указав владельца конструктору компонента. Владелец компонента (зачастую — форма) становится ответственной за уничтожение всех объектов, которыми она владеет. Другими словами, при освобождении формы освобождаются все компоненты, расположенные на ней. Поэтому при создании компонента и указании его владельца вам не надо заботиться о его удалении. Это — стандартное поведение

8

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

когда RTL среды Delphi распределяет память для строк и динамических массивов, она будет автоматически освобождать память, когда ссылка выходит за границу. Нет необходимости освобождать строки: когда строка становится недоступной, занимаемая ею память освобождается.

Однократное удаление объектов

При повторном вызове метода Free (или вызове деструктора Destroy) объекта происходит ошибка. Однако если установить объект в ноль (nil), то повторный вызов Free не вызывает проблем. Причина в том, что Free — это метод, известный в данном месте памяти, в то время как виртуальная функция Destroy определена в ходе выполнения посредством поиска типа объекта — очень опасной в случае отсутствия объекта операцией.

В качестве итога — пара рекомендаций:

для уничтожения объекта всегда вызывайте Free, а не деструктор Destroy;

используйте FreeAndNil или после вызова Free устанавливайте объектную ссылку в nil, если только ссылка сразу же не выходит за границу.

В общем случае с помощью функции Assigned также можно проверить, имеет ли объект значение nil. Эти два выражения в большинстве случаев идентичны:

if Assigned (ADate) then ...

if ADate <> nil then ...

Обратите внимание, что эти выражения лишь проверяют, является ли указатель ненулевым; они не проверяют, является ли он допустимым (действительным) указателем. При написании представленного ниже программного кода проверка будет удовлетворена, и вы получите сообщение об ошибке в строке, вызывающей метод объекта:

ToDestroy.Free;

if ToDestroy <> nil then ToDestroy.DoSomething;

Важно осознать, что вызов метода Free не устанавливает объект в ноль.

9