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

Информатика в техническом университете / Информатика в техническом университете. Объектно ориентированное программирование

.pdf
Скачиваний:
105
Добавлен:
06.03.2018
Размер:
9.48 Mб
Скачать

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

Procedure Draw;

{рисование}

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

Procedure ReColor(acolor: 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.ReColor(aColor: TColor);

Begin

Clear;

Color: =aColor; Draw; End;

End

 

 

 

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

Unit Main;

Interface

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

Type

TMainForm = class(TForm)

Bevel: TBevel;

{рамка}

Image: TImage;

{поле рисования}

ColorButton, ExitButton: TButton; {кнопки}

rEdit: TEdit;

{редактор ввода радиуса}

rLabel: TLabel;

{метка ввода радиуса}

200

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

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

ColorDialog: TColorDialog; {диалог выбора цвета}

Procedure FormActivate{Sender: TObject);

Procedure ImageMouseDown{Sender: TObject; Button: TMouseButton; Shift: TShiftState; AT, 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; VarC'.TMyCircle;

Procedure TMainForm. FormActivate{Sender: TObject);

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

End;

Procedure TMainForm.ImageMouseDown{Sender: TObject;

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

Begin

 

 

 

 

if Button=mbLeft then

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

begin

 

 

 

 

С.Free;

 

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

С: -TMyCircle.Create(Imagel

,Х, Y,strtoint(rEdit. Text),

С.Draw;

Image1.Canvas.Pen.Color);

{конструировать}

{изобразить объект с заданными параметрами}

end;

 

 

 

 

End;

 

 

 

 

Procedure TMainForm. UpDownl Click(Sender: TObject;

Begin

 

 

Button: TUDBtnType);

 

 

 

 

ifConil then

 

 

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

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

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

End;

 

 

 

 

Procedure TMainForm.ColorButtonClick(Sender:

TObject);

Begin

 

 

 

 

ifColorDialog.Execute

then {если вьшолнен диалог выбора цвета, то}

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

ifC< >nil then

 

 

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

C.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^ все же методы, перекрывающие этот метод, описьшаются со спещ1фикатором override. Если же по ошибке какой-нибудь из этих методов описать virtual, то будет создано новое семейство виртуальных полиморфных методов, родоначальником которого является метод, описанный virtual по ошибке.

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

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

202

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

ТВМ, а через специальную таблицу динамических методов (ТДМ). Такие методы описываются, соответственно, dymamic - при объявлении и override - при переопределении.

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

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

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

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

классов запрещается.

 

 

 

Методы:

Класс A

 

TBM:

Класс А

VI -virtual

Метод VI

V2 - virtual

 

Метод V2

 

Dl -dynamic

ТДМ:

 

D2 - dynamic

 

Метод Dl

 

 

Класс В

Методы:

Метод D2

VI -ovemde

 

 

Класс Б

 

Dl -oven-ide

 

 

Родитель-

 

 

ТВМ:

 

 

Метод VI

 

 

Метод V2

 

 

ТДМ:

 

 

Метод D1

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

203

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

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

^

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

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

Окно

 

 

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

Окружность

приложения

 

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

Квадрат

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

 

 

 

 

Уничтожить

 

 

i 1

Уничтожить

Ы

 

 

 

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

Пример 5.2. Использование абстрактных методов (графический

редактор «Окружности и квадраты» -

вариант 1). Попробуем изменить

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

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

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

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

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

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

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

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

13 g

Цвет

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

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

® |квадрат|

I Выход I

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

204

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

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

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.

Представленный ниже текст модуля Figxire включает описание иерархии классов в соответствии с рис. 5.8, б.

Unit Figure;

 

 

Interface

 

 

Uses extctrIs,Graphics;

 

Type TMyFigure=class

 

private

x,y,r:Word; Color: TColor;

Image:TImage;

 

procedure Clear;

 

public

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

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

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

leiil

TMyCircle

Поля:

Image, х у, г, Color Методы:

Create, ReColor,

Clear, ReSize, Draw; virtual

tlliBi

TMyFigure

Поля:

Image, x y, r, Color Методы:

Create, ReColor,

Clear, ReSize,

Draw; virtual; abstract

TMySquare

TMyCircle

TMySquare

Метод: Draw, override Метод: Draw, override

Метод: Draw, override

 

б

a

 

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

205

 

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

Procedure ReSizefar: 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; {нарисовать фигуру цветом фона - стереть}

Color: =TempColor;

 

End;

 

 

 

Procedure TMyFigure.Resize;

 

Begin

Clear;

r:=ar; Draw;

End;

Procedure TMyFigure.Recolor;

Draw; End;

Begin

Clear;

Color: =aColor;

Procedure TMyCircle.Draw;

 

Begin

Image. Canvas.Pen. Color:=Color;

 

Image. Canvas.Ellipse(x-r,y'r^'^r,y-^r);

End;

Procedure TMySquare.Draw;

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

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

End;

End.

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

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

206

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

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

function

DividefX, Y: Real): Real; overload; {вариант 1}

begin

Result: = XJY; end;

function

Divide(X, Y: Integer): Integer; overload; {вариант 2}

begin

Result: = Xdiv Y; end;

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

k:=Divide(2.5, 7.3); {вызывается вариант 1} к: =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;

Var SomeObject: T2;

 

SomeObject := T2.Create;

 

SomeObject.TestCHello!');

{параметр строка - вызвается метод 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 ActI; virtual; procedure Act2; virtual; procedure Act3; virtual; procedure Act4; virtual; procedure Act5; virtual;

end;

T2 = class(Tl) public

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

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

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

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

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

end;

var SomeObjectl: Tl; begin

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

SomeObjectl Act 1; {позднее связывание - вызывается метод класса Т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(TI) public

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

209