<<
>>

Определение класса

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

Формат описания нового класса выглядит следующим образом:

ТУре = class() private

protected public «¡общедоступные элементы класса>

published «¡опубликованные элементы класса> automated end;

Имя родителя указывать не обязательно, по умолчанию считается, что, если имя родителя не указано, то класс непосредственно наследуется от TObject.

Внутри описания класса выделяется до пяти секций.

Секция private содержит внутренние элементы, обращение к которым возможны только в пределах модуля (а не класса, как в C++), содержащего объявление класса.

Секция protected содержит защищенные элементы, которые доступны в пределах модуля, содержащего объявление класса, и внутри потомков класса.

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

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

Для визуальных компонент, вынесенных на панель компонент, информация об элементах, размещенных в этой секции, становится доступной через Инспектор Объектов.

Секция automated содержит элементы, доступ к которым также выполняется аналогично public. Но для элементов, описанных в этой секции, генерируется дополнительная информация, используемая OLE. Секцию имеет смысл объявлять для потомков класса TAutoObject.

Потомки класса могут менять область доступности всех элементов родительского класса, кроме элементов, объявленных в секции private, так как последние им не доступны.

Основной особенностью объектов Delphi является то, что они всегда являются динамическими, т.е. размещаемыми в динамической области памяти.

Соответственно, переменная типа класса по смыслу представляет собой указатель на объект, однако, по правилам Delphi, при работе с этой переменной операция разыменования «Л» не используется. Вызовы же конструктора и деструктора в такой модели становятся обязательными, так как конструктор выполняет размещение объекта в памяти, а деструктор - выгрузку из нее.

Рассмотрим пример, демонстрирующий различие старой и новой объектных моделей Pascal.

Borland Pascal 7.0:

Type pTNum = ''TNum;

TNum = Object n: integer;

constructor Init (an: integer); end;

Constructor TNum.Init; begin n: -an;

end; . . .

Var p.pTNum;

Begin

New(p, Init(5));

WriteLn(pA.n);

Disposefp)..........

Чтобы подчеркнуть изменение функций конструктора и деструктора в объектной модели Delphi, для них предлагается использовать другие имена: Create (создать) - для конструктора и Destroy (уничтожить) - для деструктора. Если конструктор или деструктор для создаваемого класса не указывается, то

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

Конструктор Create класса TObject вызывает специальный метод InstanceSize для определения размера памяти, необходимой для размещения объекта, запрашивает область памяти требуемого размера, используя процедуру Newlnstance, и инициализирует поля нулевыми значениями, используя процедуру Initlnstanse. Адрес области памяти конструктор-функция возвращает в качестве результата.

При создании собственного конструктора для разрабатываемого класса необходимо перед программированием специфических операций, предусматриваемых для конструирования объектов данного класса, обращаться к наследуемому (inherited) конструктору:

Constructor TNum. Create; begin

inherited Create; {наследуемый конструктор} n: -an; end;

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

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

Деструктор Destroy класса TObject вызывает метод CleanUpInstance для корректного завершения работы с длинными строками и другими сложными структурами данных. Затем он обращается к методу InstanceSize для определения размера объекта и освобождает память. В классе TObject деструктор объявлен виртуальным, так как с одной стороны в том же классе существует метод Free (см. ниже), который вызывает деструктор, с другой стороны сам деструктор в производных классах может быть переопределен. Следовательно, при переопределении деструктора в классе его необходимо объявлять как метод, перекрывающий виртуальный (раздел 5.2):

Destructor Destroy; override;

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

Destructor TNum. Destroy; begin

. . . {выполнение специфических операций над объектом} inherited Destroy, {вызов родительского деструктора}

end;

При работе с объектами в Бе1рЫ необходимо постоянно помнить о том, что все объекты Бе1рЫ размещены в динамической памяти, а соответствующие переменные на самом деле являются указателями на объекты.

Так, например, операция присваивания объектов соответствует операции копирования адреса, а не самого объекта. В табл. 5.1 поясняется, что происходит при выполнении некоторых операций.

Таблица5.1. Особенности работы с объектами в Delphi

На первом шаге в соответствии с описанием создаются переменные А и

В. При создании они обнуляются, т.е.

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

Чтобы уменьшить количество ошибок адресации, можно использовать вместо метода Destroy метод Free. Этот метод прежде, чем освобождать память, проверяет, был ли создан объект в памяти (сравнивает адрес, записанный в переменной, со значением nil) и, если объект не создавался, то память не освобождается. Однако во многих случаях, например, для ситуации, представленной в табл. 5.1, замена Destroy на Free не поможет избежать ошибки адресации: в данном варианте объект был создан, и адрес в переменной отличен от nil.

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

Пример 5.1. Определение класса (графический редактор «Окружности» - вариант 1). Пусть требуется разработать приложение, позволяющее рисовать на специальном поле окна приложения окружности. Местоположение центра окружности должно определяться щелчком левой клавиши мыши. Радиус окружности и цвет контура должны настраиваться.

Разрабатываемое приложение легко декомпозируется на два объекта: окно приложения и изображаемая в нем окружность (или круг, так как в процессе рисования внутреннее поле окружности заполняется точками цвета фона).

Результат декомпозиции приведен на рис. 5.1.

Рис. 5.1. Объектная декомпозиция приложения «Окружности»

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

Компоненты, использованные для формирования внешнего вида окна приложения, настраиваются с помощью Инспектора Объектов следующим образом:

Forml:

Name:=MainForm; Сарйоп:=’Графический редактор “Окружность”’;

Bevell:

Name:= Bevel;

Imagel:

Name:=Image;

Label!:

Name:=rLabel;

Сарйоп:=’Радиус круга’;

Кроме указанных компонент приложение будет использовать стандартный диалог выбора цвета. При визуальном проектировании формы этот диалог в виде условного изображения добавляется в форму и в нужный момент программно инициируется. Имя этого компонента также настраивается через Инспектор Объектов (Кате:=Со1ог01а1о§).

Интерфейс может находиться в двух состояниях: ожидание действий пользователя, связанных с рисованием, и ожидание выбора цвета (в диалоге выбора цвета). Граф состояний интерфейса, содержащий привязку к событиям, доступным при разработке приложения в Бе1рЫ, приведен на рис. 5.3.

Обработчик события С1 (Ма1пГогтАсбуа1е) должен устанавливать цвет фона и исходный цвет рисования.

Обработчик события С2 (¡п^еМоияеОоиш) определив, что нажата именно левая клавиша, должен рисовать круг с заданным радиусом текущим цветом контура и центром в точке местонахождения курсора мыши.

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

Событие С4 будет обрабатываться диалогом выбора цвета.

Обработчик события С 5 (ирБоншСиск) должен проверять, существует ли объект класса Окружность, н если такой объект существует, то стирать старое изображение и выводить новое с изменением размера.

Обработчик события С6 (ЕхПВиМопСПск) должен завершать приложение.

Диаграмма класса ТМашРопп, наследуемого от стандартного класса ТТогт и включающего объектные поля стандартных классов Ое1рЫ, приведена на рис. 5.4, а.

Рис. 5.3. Граф состояний интерфейса

Класс TMyCircle (рис. 5.4, б) в соответствии с правилами Delphi наследуется от TObject. Он добавляет поля для хранения координат центра окружности (х,у), ее радиуса г, цвета Color и имени компонента Image, на холсте которого нарисована окружность. Набор методов в основном определяется перечнем сообщений, на которые должен реагировать объект данного класса (см. рис. 5.2). Дополнительно переопределим метод Create, чтобы использовать его для инициализации полей объекта при создании. Кроме этого, целесообразно определить метод Clear для стирания нарисованной окружности при изменении ее размера или цвета.

Определим тип доступа для всех компонентов класса: все поля и метод Clear, к которому не требуется доступ извне, объявим в секции private (внутренние), а все остальные компоненты - в секции public.

Объявление класса TMyCircle выполним в отдельном модуле Circui:

Unit Circui;

Interface

Uses extctrls,Graphics ;

Type TMyCircle=class private

x, y, r: Word; {координаты центра и радиус окружности} Color: TColor; {цвет}

Image:TImage; {поле для рисования}

Procedure Clear; {стирание окружности} public

Constructor Create(aImage:TImage; ax,ay,ar:Word; aColor:TColor); {конструктор}

Procedure Draw; {рисование}

Procedure ReSize(ar: Word); {изменение размеров} Procedure ReColorfacolor: TColor); {изменение цвета}

end;

Implementation Constructor TMyCircle.Create;

Begin inherited Create; {вызвать наследуемый конструктор} Image: =almage; {инициализировать поле} x: =ax; у: =ay; r:=ar; Color: =aColor;

End;

Procedure TMyCircle.Draw;

Begin

Image.Canvas.Pen.Color: =Color; {задать цвет пера} Image.Canvas.Ellipse(x-r, y-r, x+r, y+r); {нарисовать окружность} End;

Procedure TMyCircle. Clear;

Var TempColor: TColor;

Begin

TempColor: =Color; {сохранить цвет пера}

Color: =Image. Canvas.Brush. Color; {фиксировать цвет фона} Draw; {нарисовать цветом фона - стереть }

Color:=TempColor; {востановить цвет пера}

End;

Procedure TMyCircle.ReSize;

Begin Clear; r:=ar; Draw; End;

Procedure TMyCircul.ReColoriaColor: TColor);

Begin Clear; Color: =aColor; Draw; End;

End.

Теперь переходим к программированию обработчиков событий, при наступлении которых должны выполняться основные операции приложения. Ниже приводится текст модуля Main, соответствующего форме TMainForm.

Unit Main;

Interface

Uses Windows, Messages, Sys Utils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, ComCtrls;

Type

TMainForm = class(TForm)

Bevel: TBevel; {рамка}

Image: TImage; {поле рисования}

ColorButton, ExitButton: TButton; {кнопки} rEdit: TEdit; {редактор ввода радиуса}

rLabel: TLabel; {метка ввода радиуса}

UpDowtt: TUpDown; {компонент «инкремент/декремент»} ColorDialog: TColorDialog; {диалог выбора цвета}

Procedure FormActivate(Sender: TObject);

Procedure ImageMouseDown{Sender: TObject; Button: TMouseButton;

Shift: TShiftState; X, Y: Integer);

Procedure UpDownClick(Sender: TObject; Button: TVDBtnType); Procedure ColorButtonClick(Sender: TObject);

Procedure ExitButtonClick(Sender: TObject); end;

Var MainForm: TMainForm;

Implementation {$R *.DFM}

Uses Circul;

Var C.TMyCircle;

Procedure TMainForm. FormActivate(Sender: TObject)-,

Begin Image 1. Canvas.Brush. Color: =clWhite; {установить белый фон} Imagel.Canvas.Pen.Color: =clBlack; {установить цвет рисования}

End;

Procedure TMainForm.ImageMouseDown(Sender: TObject;

Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

Begin

if Button=mbLeft then {если нажата левая клавиша мыши}

begin

C. Free; {если объект создан, то уничтожить его}

С:-TMyCircle. Create(Imagel ,Х, Y,strtoint(rEdit. Text),

Imagel.Canvas.Pen.Color); {конструировать} C.Draw; {изобразить объект с заданными параметрами} end;

End;

Procedure TMainForm. UpDownlClick(Sender: TObject;

Button: TUDBtnType);

Begin

if Cnil then {если объект создан, то}

C.ReSize(strtoint( rEdit. Text)); {перерисовать с другим радиусом}

End;

Procedure TMainForm.ColorButtonClick(Sender: TObject);

Begin

if ColorDialog.Execute then {если выполнен диалог выбора цвета, то} Imagel.Canvas.Pen.Color:=ColorDialog.Color; {установить цвет} if Cnil then {если объект создан, то}

C.ReColor(Imagel.Canvas.Pen.Color); {перерисовать другим цветом} End;

Procedure TMainForm.ExitButtonClick(Sender: TObject);

Begin Close; End;

Initialization

Finalization C.Free; {если объект создан, то уничтожить его} End.

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

5.1.

<< | >>
Источник: Г.С.Иванова, Т.Н.Ничушкина, Е.К.Пугачев. Объектно- ориентированное программирование. 2001

Еще по теме Определение класса:

  1. § 33 Общее правило о переходе наследства к детям. – Отличие отделенных от неотделенных. – Право представления. – Право родительское. – Право боковых родственников. – Римская система определения прав по классам и степеням. – Германская система определения прав по линиям и коленам.
  2. 40. Основные теоретические подходы в определении классов. Немарксистские подходы
  3. Школьный класс
  4. Одиночки в классе
  5. ПЕРВЫЙ КЛАСС
  6. ПЯТЫЙ КЛАСС
  7. 6.3. Женщина высшего класса
  8. 5. Критическая теория и рабочий класс.
  9. Глава 13. INTJ «Компетентность + независимость = высший класс»
  10. СРАВНИТЕЛЬНАЯ СИСТЕМА КЛАССОВ
  11. Масса, демократия и рабочий класс
  12. ПОТЕНЦИАЛЬНЫЕ ВОЗМОЖНОСТИ ШКОЛЬНОГО КЛАССА КАК ГРУППЫ
  13. 2. Постарайтесь вникнуть в проблему «единственный ребенок – класс»