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

книги / Объектно-ориентированное программирование

..pdf
Скачиваний:
11
Добавлен:
12.11.2023
Размер:
16.61 Mб
Скачать

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

UpDown: TUpDown; {компонент «инкремент/декремент»}

ColorDialog: TColorDialog; {диалог выбора цвета} Procedure FormActivate(Sender: TObject);

Procedure ImageMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

Procedure UpDownClick(Sender: TObject; Button: TUDBtnType); 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 Image1.Canvas.Brush.Color: =clWhite; {установить белый фон} Imagel.Canvas.Pen.Color: =clBlack; {установить цвет рисования}

End;

Procedure TMainForm.ImageMouseDown(Sender: TObject;

Button: TMouseButton; Shift: TShiftState; X, K- Integer);

Begin

ifButton=mbLeft then

{если нажата левая клавиша мыши}

begin

 

С.Free;

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

С:~TMyCircle.Create(Imagel,X, Y,strtoint(rEdit. Text),

ImageI.Canvas.Pen.Color); {конструировать}

C.Draw; {изобразить объект с заданными параметрами} end;

End;

Procedure TMainForm. UpDownlClick(Sender: TObject;

Button: TUDBtnType);

Begin

if C<>nil then

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

C.ReSize(strtoint(rEdit. Text));

{перерисовать с другим радиусом}

End;

 

Procedure TMainForm.ColorButtonClick(Sender: TObject);

Begin

if ColorDialog.Execute then {если выполнен диалог выбора цвета, то}

Imagel.Canvas.Pen.Color: =ColorDialog.Color; {установить цвет}

if C<>nil then {если объект создан, то}

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

End;

Procedure TMainForm.ExitButtonClick(Sender: TObject);

201

5. Объектная модель Delphi Pascal

Begin Close; End; Initialization

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

End.

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

всекции finalization.

5.2.Особенности реализации переопределения методов

ВDelphi Pascal реализованы более сложные механизмы переопределения методов. Прежде всего в нем различают: виртуальные методы, динамические методы и абстрактные методы. Кроме того, в последних версиях языка реализована параметрическая перегрузка как обычных процедур и функций, так и методов. Использование соответствующих средств позволяет создавать существенно более мощные классы.

Ви р ту альн ы е методы . Виртуальные методы объектной модели, используемой Delphi, практически ничем не отличаются от тех, которые определялись в Borland Pascal 7.0. Для них также реализуется механизм позднего связывания, обеспечивая возможность построения виртуальных объектов. Не изменилась и сущность реализации механизма позднего связывания через ТВМ. Изменения коснулись лишь описания виртуальных методов. Теперь только самый первый виртуальный метод в иерархии описывается virtual, все же методы, перекрывающие этот метод, описываются со спецификатором override. Если же по ошибке какой-нибудь из этих методов

описать virtual, то будет создано новое семейство виртуальных полиморфных методов, родоначальником которого является метод, описанный virtual по ошибке.

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

Д и н а м и ч е с к и е м етоды . Динамическими в D elphi назы ваю тся полиморфные виртуальные методы, доступ к которым выполняется не через

202

a b s t r a c t

5.2. Особенности реализации переопределения методов

ТВМ, а через специальную таблицу динамических методов (ТДМ). Такие методы описываются, соответственно, d y m a m ic - при объявлении и o v e r r id e - при переопределении.

В отличие от ТВМ, которая хранит адреса всех виртуальных методов данного класса, ТДМ хранит только адреса виртуальны х методов, определенных в данном классе (рис. 5.5). Если осущ ествляется вызов динамического метода, определенного в предках данного класса, то его адрес отыскивается в организованных в списковую структуру описаниях классов иерархии (ТВМ - раздел 5.6). Естественно, поиск такого метода займет больше времени по сравнению с виртуальными методами. Однако ТДМ занимает меньше места, так как не хранит всех предшествующих ссылок.

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

А б страктн ы е м етоды . А б с т р а к т н ы е методы использую тся при объявлении методов, реализация которых откладывается. Такие методы в классе описы ваю тся служ ебны м словом и обязательно переопределяются в потомках класса.

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

Класс А

Класс В

Методы:

V I - virtual V2 - virtual D1 - dynamic D2 - dynamic

Методы:

V 1 - override D1 - override

Класс A

TBM:

Метод V 1 Метод V2

ТДМ:' Метод D1 Метод D2

Класс Б Родитель-

ТВМ: Метод V 1 Метод V2

ТДМ: Метод D1

Рис. 5.5. Виртуальные и динамические методы

203

5. Объектная модель Delphi Pascal

Создать и нарисовать

 

Создать и нарисовать

< -------------------------

 

-------------------------- >|

Изменить размер

Окно

Изменить размер

Окружность

приложения

Квадрат

Изменить цвет

 

Изменить цвет

Уничтожить

 

Уничтожить

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

Пример 5.2. Использование абстрактных методов (графический редактор «Окружности и квадраты» - вариант 1). Попробуем изменить пример предыдущего раздела так, чтобы можно было рисовать не только окружности, но и, например, квадраты.

Диаграмма объектов такого приложения изображена на рис. 5.6. Естественно, теперь интерфейс должен обеспечивать возможность

выбора фигуры. С этой целью добавим в форму компонент TRadioGroup группу «радиокнопок» (рис. 5.7).

Соответственно при обработке события С2 (см. рис.5.3) необходимо учитывать тип выбранной фигуры.

Класс TMainForm будет иметь ту же структуру, что и в предыдущем примере, но в него будет добавлено поле RadioGroup типа TRadioButton.

Класс TMySquare можно построить несколькими способами.

J *' Графический редактор "Окружности и квадраты"

IE

Радиус круга:

Цвет

-Вид фигуры:—

ООкружность TRadioGroup

<§) |Квадрат

Выход

Рис. 5.7. Вид главного окна приложения «Окружности и квадраты»

204

5.2.Особенности реализации переопределения методов

1.Класс ТМуSquare можно наследовать от TObject, так же как был получен классТМ уС ш Л е. В этом случае нам придется повторить программирование всех методов данного класса, что явно нецелесообразно.

2.Класс TMySquare можно наследовать от TMyCircle (рис.5.8, а). Тогда мы можем наследовать от TMyCircle методы Create, Clear, ReColor, ReSize, определяющие общие элементы поведения. Метод Draw необходимо объявить виртуальным, так как он вызывается из наследуемых методов и переопределен

вклассе-потомке. Метод Draw класса TMySquare должен быть объявлен переопределенным - override. Данный вариант наследования, принципиально применимый в конкретном случае, является не универсальным (и, в общем, не логичным: как можно «наследовать» от круга квадрат?).

3.С учетом существования двух типов объектов со сходным поведением можно создать абстрактный класс TFigure, инкапсулирующий требуемый тип поведения фигур (рис. 5.8, б). В этом классе нужно объявить метод Draw виртуальным (virtual) и абстрактным (abstract) и определить методы Create, Clear, ReColor, ReSize через метод Draw. Теперь мы можем наследовать от этого абстрактного класса классы, рисующие любые фигуры. Эти классы должны будут переопределять абстрактный метод Draw класса TMyFigure.

Представленный ниже текст модуля Figure включает описание иерархии

классов в соответствии с рис. 5.8, б.

Unit Figure;

Interface

Uses extctrls, Graphics; Type TMyFigure=class

private x,y,r:Word; Color:TColor; Image:Tlmage; procedure Clear;

public

Constructor Create(almage: Tlmage;ax,ay,ar: Word;aColor:TColor); Procedure Draw; virtual; abstract; {абстрактная процедура}

Простая иерархия

Иерархия с абстрактным классом

Метод: Draw, override

б

а

Рис. 5.8. Два варианта иерархии классов

205

 

 

 

5. Объектная модель Delphi Pascal

Procedure ReSize(ar:Word);

 

 

 

 

Procedure ReColor(acolor: TColor);

 

 

 

 

end;

 

 

 

 

 

 

 

TMyCircle=class(TMyFigure)

{класс Окружность}

public

Procedure Draw; override;

(рисование окружности}

end;

 

 

 

 

 

 

 

TMySquare=class(TMyFigure)

 

(класс Квадрат}

public

Procedure Draw; override;

(рисование квадрата}

end;

 

 

 

 

 

 

 

Implementation

 

 

 

 

 

Constructor TMyFigure.Create;

 

 

 

 

Begin

 

 

 

 

 

 

 

inherited Create;

 

 

 

 

Image:=almage; x:=ax; y:-ay;

r:-ar;

Color: -aColor;

End;

 

 

 

 

 

 

 

Procedure TMyFigure.Clear;

 

 

 

 

Var TempColor:TColor;

 

 

 

 

Begin

TempColor:=Color;

 

 

 

 

 

Color: =Image.Canvas.Brush.Color;

 

 

Draw; (нарисовать фигуру цветом фона - стереть}

End;

Color:= TempColor;

 

 

 

 

 

 

 

 

 

 

 

Procedure TMyFigure.Resize;

 

 

 

 

Begin

Clear;

r: =ar; Draw;

 

End;

 

Procedure TMyFigure.Recolor;

 

 

 

 

Begin

Clear;

Color: =aColor;

 

 

Draw;

End;

Procedure TMyCircle.Draw;

 

 

 

 

Begin

Image.Canvas.Pen.Color:=Color;

 

 

Image.Canvas.Ellipse(x-r,y-rpc+r,y+r);

 

End;

Procedure TMySquare.Draw;

Begin Image.Canvas.Pen.Color:=Color;

Image.Canvas.Rectangle(x-r,y-rpc+r,y+r);

End;

End.

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

П р и м е ч а н и е . При создании аналогичных иерархий средствами Borland Pascal 7.0

приходилось использовать «пустые» методы, включающие только операторные скобки begin end. При этом компилятор не мог контролировать создание объектов абстрактных классов, предоставляя разработчику самостоятельно отыскивать ошибки данного вида.

206

5.2. Особенности реализации переопределения методов

Перегрузка методов. Начиная с версии 4, в Delphi Pascal появилась возможность перегрузки процедур и функций:

function Divide(X, Y: Real): Real; overload; {вариант 1} begin Result:= X/Y; end;

function Divide(X,

Y: Integer): Integer; overload; {вариант 2}

begin

Result :

=Xdiv Y; end;

Какой конкретно вариант перегруженных процедур и функций вызван, определяется по типам фактических параметров:

к:=Divide(2.5, 7.3); {вызывается вариант 1} k:=Divide(6,3); {вызывается вариант 2}

Перегружать можно не только простые процедуры и функции, но и методы. Перегруженный метод не перекрывается как переопределенный, а

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

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

type

Т1 - class(TObject) public

procedure Test(I: Integer); overload; end;

T2 = class(Tl) public

procedure Test(S: string); overload; end;

VarSomeObject: T2;

SomeObject := T2.Create;

SomeObject. Test('Hello!);

{параметр строка - вызвается метод Test класса

SomeObject.Test(7);

Т2}

{параметр целое число-вызывается метод Test

 

класса Т1}

В отличие от перегруженных методов C++ перегруженные методы Delphi Pascal не могут объявляться внутри одного класса:

Туре

TSomeClass = class

207

5. Объектная модель Delphi Pascal

public

function Func(P: Integer): Integer;

function Func(P: Boolean): Integer { ошибка!}

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

Рассмотрим различные варианты переопределения виртуальных методов:

Туре

Т1 = class(TObject)

public procedure Actl; virtual; procedure Act2; virtual; procedure Act3; virtual; procedure Act4; virtual; procedure Act5; virtual;

end;

T2 = class(Tl) public

procedure A ctl;override; {определение нового аспекта виртуального полиморфного метода}

procedure Act2;virtual; {определяется новый полиморфный метод - компилятор выдает предупреждение о возможной ошибке, так как при этом доступ к ранее описанному полиморфному методу перекрывается}

procedure Act3;reintroduce;virtual; {определяется новый полиморфный метод - компилятор предупреждения не выдает, так как пресечение доступа документировано}

procedure Act4; {переопределение виртуального метода простым - компилятор выдает предупреждение о возможной ошибке, так как при этом доступ к виртуальному методу перекрывается}

procedure Act5; reintroduce; {переопределение виртуального метода простым - компилятор предупреждения не выдает, так как пресечение доступа документировано}

end;

var SomeObjectl: Tl; begin

SomeObjectl . = T2.Create; {указатель на базовый класс содержит адрес объекта производного класса}

SomeObjectl.Actl; {позднее связывание - вызывается метод класса Т2} SomeObjectl.Act2; {раннее связывание - вызывается метод класса Т1}

208

5.2. Особенности реализации переопределения методов

SomeObjectl.Act3; {раннее связывание - вызывается метод класса Т1} SomeObjectl.Act4; (раннее связывание - вызывается метод класса Т1} SomeObjectl.Act5; {раннее связывание - вызывается метод класса Т1} end;

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

type

Т1 = class(TObject) public

procedure Test(I: Integer); overload; virtual; end;

T2 = class(Tl) public

procedure Test(S: string); reintroduce; overload; end;

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

procedure Confused(I: Integer); overload; Begin... End;

procedure Confused (I: Integer; J: Integer = 0); overload; Begin... End;

Confused(X); {ошибка компиляции, не определен вариант перегруженной процедуры}

Аналогичная ситуация при перегрузке методов не приводит к появлению сообщения об ошибке, просто один из перегруженных методов становится недоступным (в примере ниже недоступен перегруженный метод базового класса):

Туре

Т1 = class(TObject) public

procedure Test(I: Integer); overload; virtual; end;

T2 = class(Tl) public

procedureTest (I:Integer;S:string= 'aaa ’); reintroduce; overload; end;

209

5. Объектная модель Delphi Pascal

SomeObject := T2.Create;

SomeObject.Test(5,'Hello!'); {параметры целое число и строка - вызвается метод Test класса Т2}

SomeObject.Test(7'); {параметр целое число - вызывается метод Test класса Т2 с параметром по умолчанию}

Перегрузить можно любой метод, в том числе и конструктор:

Type Tl=class

public I:integer;

Constructor Create;overload; end;

T2=class(Tl) public

Constructor Creale(aI:Integer);overload; end;

Constructor Tl. Create; Begin

inherited Create; I: =10;

End;

Constructor T2.Create(al:Integer); Begin

inherited Create; I:=al;

End;

VarSomeObject: T2;

SomeObject:=T2.Create; {вызывается конструктор базового класса - в поле I записано 10}

SomeObject:=Т2.Create(5); {вызывается конструктор производного класса - в поле I записано 5}

5.3. Свойства

Свойство - это средство Pascal Delphi, позволяющее определять интерфейс доступа к полям класса.

В Delphi различают:

простые или скалярные свойства; свойства-массивы;

индексируемые свойства или свойства со спецификацией index; процедурные свойства.

210