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

Программирование. Модуль GRAPH. Модульное и объектное программирование. Учебно-методическое пособие

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

51

Правило 1

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

Правило 2

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

Правило 3

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

Правило 4

Дочерний тип может доопределить произвольное число собственных методов и информационных полей.

Правило 5

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

Правило 6

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

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

Program DemoObgectStaya; Uses

Graph,CRT;

 

Type

 

Mother = Object;

{Родительский объект}

x,y:Integer;

vver,pole,vnis:Pointer;

Mize,Size,Lize,ax1,ay1:Integer;

ch:Char;

52

Procedure Draw(xa,ya:Integer);

Procedure Mahi(bx,by:Integer);

Procedure Polet(ax,ay:Integer);

End;

Doter = Object(Mother) {Дочерний объект} Procedure DrawMin(k:Real;xa,ya:Integer);

End;

Procedure Mother.Draw(xa,ya:Integer);

Begin

{Рисование и ввод в динамическую память}

SetColor(14);

{родительской птицы}

SetFillStyle(1,14);

 

FillEllipse(xa,ya,10,10);

Line(xa-10,ya,xa-30,ya);

Line(xa+10,ya,xa+30,ya);

Line(xa-30,ya,xa-50,ya-20);

Line(xa+30,ya,xa+50,ya-20); Size:=ImageSize(xa-50,ya-20,xa+50,ya+20); GetMem(vver,size); GetImage(xa-50,ya-20,xa+50,ya+20,vver^); ClearDevice

FillEllipse(xa,ya,10,10);

Line(xa-10,ya,xa-30,ya);

Line(xa-10,ya,xa+30,ya); Line(xa-30,ya,xa-50,ya+20); Line(xa+30,ya,xa+50,ya+20); Lize:=ImageSize(xa-50,ya-20,xa+50,ya+20); GetMem(vniz,lize); GetImage(xa-50,ya-20,xa+50,ya+20,vniz^); ClearDevice:

End;

Procedure Mother.Mahi(bx,by:Integer); Begin {Задание взмахов птицы} Mize:=ImageSize(bx,by,bx+100,by+40); GetMem(pole,mize); GetImage(bx,by,bx+100,by+40,pole^); PutImage(bx,by,vver^,1);

Delay(70);

PutImage(bx,by,pole^,0);

PutImage(bx,by,vniz^,1);

Delay(70);

PutImage(bx,by,pole^,0);

PutImage(bx,by,vniz^,1);

Delay(70);

53

PutImage(bx,by,pole^,0);

End;

Procedure Mother.Polet(ax,ay:Integer);

Begin {Управление полетом птицы} If (ax=0) And (ay=0) Then

Begin

ax:=ax1;

ay:=ay1;

End;

Repeat

If KeyPressed Then Begin

ch:=ReadKey; Case ch Of

#77:ax:=ax+10; #75:ax:=ax-10; #80:ay:=ay+10; #72:ay:=ay-10;

End;

Mahi(ax,ay);

End;

Else Mahi(ax,ay);

Until ch=#32; ax1:=ax; ay1:=ay; End;

Procedure Doter.DrawMin(k:real;xa,ya:Integer);

Begin {Рисование и ввод в динамическую память}

SetColor(14); {дочерней птицы} SetFillStyle(1,14); FillEllipse(xa,ya,Round(10/k),Round(10/k)); Line(xaRound(10/k),ya,xaRound(30/k),ya); Line(xa+ Round(10/k),ya,xa+ Round(30/k),ya);

Line(xaRound(30/k),ya,xaRound(50/k),yaRound(20/k)); Line(xa+ Round(30/k),ya,xa+ Round(50/k),yaRound(20/k));

Size:=ImageSize(xaRound(50/k),yaRound(20/k),xa+ round (50/k),ya+ Round(20/k));

GetMem(vver,size); GetImage(xa-Round(50/k),ya-Round(20/k),xa+Round(50/k),ya+

Round(20/k),vver^); ClearDevice

FillEllipse(xa,ya, Round(10/k), Round(10/k));

Line(xaRound(10/k),ya,xaRound(30/k),ya);

54

Line(xaRound(10/k),ya,xa+ Round(30/k),ya);

Line(xaRound(30/k),ya,xaRound(50/k),ya+ Round(20/k)); Line(xa+ Round(30/k),ya,xa+ Round(50/k),ya+ Round(20/k));

Lize:=ImageSize(xaRound(50/k),yaRound(20/k),xa+ Round (50/k),ya+ Round(20/k));

GetMem(vniz,lize);

GetImage(xaRound(50/k),yaRound(20/k),xa+ Round(50/k), ya+ Round(20/k),vniz^);

ClearDevice:

End;

Var

GrDriver, GrMode,alx,aly:Integer; gol:Mother;vorob:Doter;

Begin GrDriver:= Detect;

InitGraph (GrDriver, GrMode, 'C: \Bp\BGI'); Readln;

Gol.Draw(100,100);

Vorob.drawmin(3,400,400);

Gol.Polet(100,100);

alx:=Gol.ax1;

aly:=Gol.ay1;

Vorob.polet(alx,aly);

CloseGraph;

End.

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

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

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

Конструкторы предназначены для создания конкретного экземпляра объекта, ведь объект – это тип данных, т.е. «шаблон», по которому можно создать множество рабочих экземпляров данных объектного типа. Зарезервированное слово constructor используется в заголовке конструктора вместо слова procedure и предписывает компилятору создать особый код пролога, с помощью которого настраивается так называемая таблица виртуальных методов.

55

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

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

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

Program DemoObgect; Uses

Graph,CRT; Type

Ptica = Object; x,y,xa,ya,bx,by,ax,ay:Integer; vver,pole,vnis:Pointer; Mize,Size,Lize:Integer; ch:Char;

Constructor Init;

Procedure Draw; Procedure Mahi; Virtual; Procedure Polet; Virtual;

End;

Constructor Ptica.Init; Begin

xa:=100;

ya:=100;

bx:=ax;

by:=ay;

End;

Procedure Ptica.Draw; Begin

Ptica.init;

SetColor(14);

SetFillStyle(1,14);

FillEllipse(xa,ya,10,10);

Line(xa-10,ya,xa-30,ya);

Line(xa+10,ya,xa+30,ya);

56

Line(xa-30,ya,xa-50,ya-20);

Line(xa+30,ya,xa+50,ya-20); Size:=ImageSize(xa-50,ya-20,xa+50,ya+20); GetMem(vver,size); GetImage(xa-50,ya-20,xa+50,ya+20,vver^); ClearDevice;

FillEllipse(xa,ya,10,10);

Line(xa-10,ya,xa-30,ya);

Line(xa-10,ya,xa+30,ya); Line(xa-30,ya,xa-50,ya+20); Line(xa+30,ya,xa+50,ya+20); Lize:=ImageSize(xa-50,ya-20,xa+50,ya+20); GetMem(vniz,lize); GetImage(xa-50,ya-20,xa+50,ya+20,vniz^); ClearDevice:

End;

Procedure Ptica.Mahi; Begin

Ptica.Init;

Mize:=ImageSize(bx,by,bx+100,by+40);

GetMem(pole,mize);

GetImage(bx,by,bx+100,by+40,pole^);

PutImage(bx,by,vver^,1);

delay(70);

PutImage(bx,by,pole^,0);

PutImage(bx,by,vniz^,1);

delay(70);

PutImage(bx,by,pole^,0);

PutImage(bx,by,vniz^,1);

delay(70);

PutImage(bx,by,pole^,0);

End;

Procedure Ptica.Polet; Begin

Repeat

If KeyPressed Then Begin

ch:=ReadKey; Case ch Of

#77:ax:=ax+10; #75:ax:=ax-10; #80:ay:=ay+10; #72:ay:=ay-10;

57

End;

Ptica.init;

Mahi;

End;

Else

Begin

Ptica.Init;

Mahi;

End;

Until ch=#32; End;

Var

GrDriver, GrMode,alx,aly:Integer; gol:Ptica;

Begin GrDriver:=Detect;

InitGraph (GrDriver, GrMode, 'C: \Bp\BGI'); Readln;

Gol.init;

Gol.Draw;

Gol.Polet;

Readln;

CloseGraph;

End.

При трансляции объекта, содержащего виртуальные методы, создается так называемая таблица виртуальных методов (ТВМ), количество элементов которой равно количеству виртуальных методов объекта. В этой таблице будут храниться адреса точек входа в каждый виртуальный метод. В нашем примере ТВМ объекта Ptica хранит два элемента – адреса методов Mahi и Polet.

Необходимо отметить особо, что в качестве имен конструкторов объектов, так же как и для процедур инициализации, рекомендуется использовать идентификатор Init.

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

Применительно к статическим и динамическим методам различают термины: ранняя связь и поздняя связь.

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

58

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

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

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

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

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

3.5. Отличие виртуальных и динамических методов

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

Синтаксически, в отличие от виртуальных методов, для динамических методов в заголовке дополнительно указывается индекс динамического метода, который располагается непосредственно за ключевым словом Virtual. Индекс динамического метода должен быть целочисленной константой в диапазоне от 1 до 656535 и, кроме того, не должен повторяться среди индексов других динамических методов, которые описаны в данном объектном типе и/или его родительских типах.

Пример: Procedure Move (C1:char,x,y:integer ); virtual77;

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

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

59

3.6. Деструкторы

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

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

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

Пример деструктора.

Destructor Done; Begin

CloseGraph;

End;

Для освобождения динамической памяти можно использовать процедуру Freemem.

FREEMEM(P,SIZE);

Здесь, P – нетипизированный указатель, SIZE – размер в байтах освобождаемой части кучи.

Использование процедур GETMEM - FREEMEM, как и вся работа с динамической памятью, требует особой осторожности и тщательного соблюдения простого правила: освобождать нужно ровно столько памяти, сколько ее было зарезервировано, и именно с того адреса, с которого она была зарезервирована.

Рассмотрим наследование, работу конструктора и деструктора на примере, приведенном выше.

Program DemoObgectStaya; Uses

Graph,CRT; Type

Mother = Object;

60

k,x,y,xa,ya,bx,by,ax,ay:Integer;

vver,pole,vnis:Pointer;

Mize,Size,Lize,ax1,ay1:Integer;

ch:Char; Constructor InitD; Procedure Draw;

Procedure Mahi; Virtual;

Procedure Polet; Virtual; Destructor Done;

End;

Doter = Object(Mother) Constructor Init; Procedure DrawMin;

End;

Constructor Mother.Init; Begin

xa;=100;

ya:=100;

bx:=ax;

by:=ay;

ax1:=ax;

ax1:=ax;

End;

Constructor Doter.InitD; Begin

k:=3;

xa;=100;

ya:=100;

ax:=ax1;

ay:=ay1;

bx:=ax;

by:=ay;

End;

Destructor Mother.Done; Begin

FreeMem(Vver,Size);

FreeMem(Vniz,Lize);

End;

Procedure Mother.Draw; Begin

Mother.Init;

SetColor(14);

SetFillStyle(1,14);

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