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

Алгоритмические языки высокого уровня. Курс лекций

.pdf
Скачиваний:
0
Добавлен:
12.08.2026
Размер:
940 Кб
Скачать

TCircle наследует метод MoveTo, но не переопределяет его. По- этому при вызове метода MoveTo экземпляром объекта TCircle он, в свою очередь, вызовет методы Show и Hide объекта TPoint, т.е. переместит точку, а не окружность.

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

Show и Hide виртуальными.

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

дается так называемая таблица виртуальных методов (ТВМ), со-

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

virtual.

Новые объявления методов TPoint и TCircle:

Type

TPoint = object(TLocation) Clr : Byte; {Цвет}

Visib : Boolean; {Видимость}

Constructor Init (InitX, InitY:Word; InitColor:Byte); {Переопределяем метод инициализации - добавляем цвет} Function GetColor : Byte; {Возвращает цвет} Procedure Show; virtual;

Procedure Hide; virtual; Procedure IsVisib : Boolean;

Procedure ChangeColor(NewColor : Byte); {Меняет цвет} Procedure MoveTo(NewX, NewY:Word);

{Перемещает в новую позицию} Destructor Done; virtual;

end;

TCircle = object(TPoint) R : Word;

Constructor Init(InitX, InitY, InitR : Word; InitC : Byte);

Procedure Show; virtual; Procedure Hide; virtual; Destructor Done; virtual;

141

end;

Объявление виртуального метода в каком-либо родительском объектном типе накладывает следующие ограничения на все его до- черние типы:

все методы дочерних типов, одноименные с виртуальными ро- дительскими, обязаны быть также виртуальными (нельзя переопре- делить виртуальный метод статическим);

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

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

Замечание: при объявлении метода Init используется constructor, Done destructor. Дело в том, что таблица виртуальных методов изначально не содержит конкретных адресов точек входа. Перед использованием любого из виртуальных методов ее надо заполнить. Делает это специальный метод-конструктор. Метод-конструктор это разновидность метода-процедуры. Син- таксически он отличается только использованием служебного слова constructor вместо procedure. Однако это приводит к тому, что при компиляции к этому методу добавляется так называемый пролог, код которого как раз и «расставляет» в ТВМ правильные адреса виртуальных методов.

Конструкторов в объекте может быть сколько угодно, один из конструкторов обязательно должен быть вызван перед вызовом пер- вого виртуального метода (иначе программа попросту «зависнет»), и конструктор сам не может быть виртуальным. Деструктор в отличие от конструктора вполне может быть виртуальным.

Type {Статический полиморфизм}

A=object;

Procedure out;

End;

B=object A

Procedure out;

End;

Procedure A.out;

Begin

Write(“A”);

End;

Procedure B.out;

Begin

142

Write(“B”);

End;

Var A1: A; B1: B; Begin

A1.out;

B1.out;

End. Результатом работы программы будет: А B Type {Динамический полиморфизм} A=object;

Procedure out; Function outs:string;

End; B=object A

Function out:string; End;

Procedure A.out; Begin

outs;

End;

Function A.outs; Begin

Outs:= “A”; End;

Function B.outs; Begin

Outs:= “B”; End;

Var A1: A; B1: B; Begin

A.out;

B.out;

End. Результатом работы этой программы будет: A А Type

A=object; Procedure out;

Function outs:string; Constructor init;

End; B=object A

Function out:string;virtual; Constructor init;

End;

Constractor A.Init Begin

End.

Procedure A.out; Begin

outs;

End;

143

Function A.outs;

Begin

Outs:= “A”;

End;

Constractor В.Init

Begin

End.

Function B.outs;

Begin

Outs:= “B”;

End;

Var A1: A; B1: B;

Begin

A.Init;

B.Init;

A.out;

B.out;

End. Результатом работы этой программы будет: A B

7.5. Использование экземпляров объектов

Поскольку объект это тип данных, то для работы с ним нужно объявить переменную этого типа. Такая переменная в рамках ООП называется экземпляром объекта. Экземпляр объекта может быть объявлен для любого объектного типа иерархии (как для родителя, так и для потомка). Более того, может быть объявлено несколько эк- земпляров одного и того же объекта. Каждый из таких экземпляров получает «свой» набор свойств. Они ничего «не знают» друг о друге, т.е. являются полностью независимыми (по крайней мере, должны таковыми быть).

Экземпляр объекта описывается в разделе описания переменных программы:

Var

<имя переменной (экземпляр объекта)>:<объектный тип>;

Объявлять можно как единичные экземпляры объектов, так и мас- сивы объектов, записи, содержащие объекты, и даже объекты, со- держащие объекты (правда, только в разделе описания типов Type). Единственное, чего нельзя делать это объявлять файлы объектов:

Var

a: Array [1..10] of TPoint; {Массив объектов - точек} b = record {Запись, содержащая два объекта}

p1,p2: TPoint; end;

f : file of TPoint; {Файл объектов - так нельзя}

144

Type

Rect = object {Объект, поля которого - объекты} a,b: TLocation;

end;

Описание экземпляра объекта TPoint (т.е. точки на экране):

Var Point1: TPoint;

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

Point1.X := 65;

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

Point1.Init(150,100,Blue); создан объект точка с коор-

динатами (X=150,Y=100) синего цвета (невидимая). Для «проявле- ния» ее на экране необходимо вызвать метод Show:

Point1.Show;

Для перемещения этой точки в новую позицию необходимо вы- звать метод MoveTo, для получения текущих координат GetX,

GetY или GetCoords и т.п.

7.5.1. Объекты и динамическая память

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

Размещение динамической памяти экземпляра объекта TCircle:

145

Type

PCircle = ^TCircle; Var

Circ1 : PCircle;

...

Begin

Circ1 := New(PCircle); или New(Circ1);

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

Circ1^.Init(150,200,40,Blue);

Существует расширенный синтаксис процедуры (и функции) New для выделения памяти экземпляру объекта с одновременным вызо- вом метода-конструктора:

Сirc1 := New(PCircle,Init(150,200,40,Blue));

или

New(Сirc1,Init(150,200,40,Blue));

При работе с динамическими объектами надо очень серьезно под- ходить к освобождению памяти. Ведь и сам объект в качестве своих полей может содержать указатели и ссылки. Память, занятая этими динамическими данными, должна освобождаться до вызова процеду- ры Dispose для самого объекта. Если память, занятая полями, осво- бождается в методе Done, то этот метод должен быть вызван до уда- ления экземпляра объекта из кучи. То, что экземпляр объекта- родителя и экземпляр объекта-потомка являются совместимыми по типу «сверху вниз» (значение экземпляра объекта-потомка может быть присвоено экземпляру объекта-предка с потерей «лишних дан- ных», но не наоборот), при работе с кучей также может привести к неверному освобождению памяти (может быть освобождена «чужая» память). В связи с этим синтаксис процедуры Dispose тоже расши- рен и позволяет вызывать деструктор объекта до его удаления из кучи:

Dispose(Сirc1,Done);

При таком написании сначала вызывается деструктор, а затем вы- свобождается память. Назначение деструктора как разновидности процедуры следить за тем, чтобы высвобождалась соответствую- щая память. Деструктор это, как и конструктор, специальный вид метода, который по-другому еще называют «сборщиком мусора».

146

7.5.2. Объекты и модули

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

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

Создание модуля графических объектов GraphObj.

Unit GraphObj; {Модуль графических объектов} INTERFACE {Интерфейсная часть}

Type

PLocation = ^TLocation; TLocation = object

X,Y: Word; {Координаты позиции на экране} Constructor Init(InitX, InitY: Word); {Инициализация

объекта}

Function GetX: Word; {Возвращает координату Х} Function GetY: Word; {Возвращает координату Y}

Procedure GetCoords(Var CoordX, CoordY: Word); {Возвращает обе координаты}

Destructor Done; virtual; {Уничтожает объект} End;

PPoint = ^TPoint;

TPoint = object(TLocation) Clr: Byte; {Цвет}

Visib: Boolean; {Видимость}

Constructor Init(InitX,InitY:Word; InitColor:Byte); {Переопределяем метод инициализации-добавляем цвет}

Function GetColor: Byte; {Возвращает цвет} Procedure Show; virtual;

Procedure Hide; virtual; Function IsVisib: Boolean;

Procedure ChangeColor(NewColor:Byte); {Меняет цвет} Procedure MoveTo(NewX, NewY: Word);

{Перемещает в новую позицию} Destructor Done; virtual;

end;

PCircle = ^TCircle; TCircle = object(TPoint) R: Word; {Радиус}

Constructor Init(InitX,InitY,InitR:Word;InitC:Byte);

147

Procedure Show; virtual; Procedure Hide; virtual; Destructor Done; virtual;

end;

IMPLEMENTATION {Раздел реализации}

Uses Graph; {Подключаем графическую библиотеку} Constructor TLocation.Init;

Begin

X := InitX;

Y := InitY end;

Function TLocation.GetX: Word; begin

GetX := X end;

Function TLocation.GetY: Word; begin

GetY := Y end;

Procedure TLocation.GetCoords; begin

CoordX := GetX;

CoordY := GetY end;

Destructor TLocation.Done; begin

end;

Constructor TPoint.Init; begin

inherited Init(InitX, InitY); Clr := InitColor;

Visib := False end;

Function TPoint.GetColor: Byte; begin

GetColor := Clr end;

Procedure TPoint.Show; begin

PutPixel(X, Y, Clr); Visib := True

end;

Procedure TPoint.Hide; begin

PutPixel(X, Y, GetBkColor); Visib := False

end;

Function TPoint.IsVisib: Boolean; begin

IsVisib := Visib

148

end;

Procedure TPoint.ChangeColor; begin

Clr := NewColor;

If IsVisib then Show end;

Procedure TPoint.MoveTo; Var St: Boolean;

begin

St := IsVisib; Hide;

X := NewX;

Y := NewY;

If St then Show end;

Destructor TPoint.Done; begin

Hide;

Clr := GetBkColor end;

Constructor TCircle.Init; begin

inherited Init(InitX, InitY, InitC); R := InitR

end;

Procedure TCircle.Show; begin

SetColor(Clr);

Circle(X, Y, R); Visib := True

end;

Procedure TCircle.Hide; begin

SetColor(GetBkColor); Circle(X, Y, R); Visib := False

end;

Destructor TCircle.Done; begin

inherited Done; R := 0

end;

{Пустая секция инициализации} END.

Использование ООП и хороший стиль программирования предпо- лагают создание объектов-программ (объектов-приложений). Такой объект (назовем его Application) должен уметь выполнять три

149

действия: инициализация (Init), выполнение основной работы (Run) и завершение работы (Done).

Пример программы

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

мяти) и TCircle.

Program AppExample;

Uses Graph, CRT, GraphObj; Type

TApplication = object Procedure.Init; Procedure.Run; Procedure.Done;

end; Var

Application: TApplication; p: PPoint;

c: TCircle;

Procedure TApplication.Init; Var

D, R, MaxX, MaxY: Integer; Begin

{Инициализация графического режима} D := Detect;

InitGraph(D, R, 'C:\BP'); SetBkColor(White);

ClearDevice;

{Создаем точку и окружность} MaxX := GetMaxX;

MaxY := GetMaxY; Randomize;

New(p,Init(Random(MaxX), Random(MaxY),Red)); p^.Show;

c.Init(Random(MaxX-40)+20,Random(MaxY-40)+20,20,Blue); c.Show

End;

Procedure TApplication.Run; Var

MaxX, MaxY: Integer; Begin

MaxX := GetMaxX;

MaxY := GetMaxY; Repeat

p^.MoveTo(Random(MaxX),Random(MaxY));

150

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]