Основы программирования (947332), страница 57
Текст из файла (страница 57)
11.15. Объектыние наследования, как в предыдущем примере,Комната Д {а) исоответственно строим один на базе второго.Трехмерная комната Д (б)Иерархия классов приведена на рис. 11.16.Необходимость использования сложного полиморфизма для методаSquare в разрабатываемой программе связана с тем, что при выполнениипрограммы доступ к объекту производного класса выполняется через указатель базового класса. При компиляции программы тип объекта считается соответствующим типу указателя, что в данном случае становится неверным.Поэтому подключение переопределенного метода Square следует выполнятьчерез ТВМ, чтобы требуемый метод определялся на этапе выполнения программы.В настоящем примере не будем конструировать объект в динамическойпамяти и освобождать память после работы с ним, так как создание и уничтожение динамических полиморфных объектов имеет свои особенности, которые будут рассмотрены в параграфе 11.6.
Вместо этого осуществим доступчерез указатель к статически созданному объекту. Кроме того, поскольку следующий пример будет работать с теми же классами,поместим описание классов в модуль.TRoomDUnit RoomMod;InterfaceTypeTRoomD=objectlengthwidth:real;{nonя: длина и ширинакомнаты}function Square:real; virtual; {метод определения площади}constructor Init(lyW:real); {конструктор}end;length, widthSquare(), Init()z^TVRoomDheightSquare(), Init()Рис. 11.16.
Иерархияклассов дляреализации объектов341Часть 2. Объектно-ориентирован и ое программированиеТуре TVRoomD = object(TRoomD)height:real;{дополнительное поле класса}function Square:real;virtual; {виртуальный полиморфный метод}constructor Init(lyWyh:real); {конструктор}end;ImplementationFunction TRoomD.Square;{тело метода определения площади}BeginSquare;= length"^ width;End;Constructor TRoomD.Init;{тело конструктора}Beginlength: Ч;width: ==w;End;Constructor TVRoomDJnit;Begininherited Init(l,w); {инициалтирует поля базового класса}height: =h; {инициализируем собственное поле класса}End;Function TVRoomD, Square;BeginSquare: ^inherited Square+2 '^height*(length+ width);End;End.Тогда основная программа будет выглядеть следующим образом.Program case2;Uses RoomMod;VarpA: ^TRoomD; {объявляем указатель на объекты класса}В:TVRoomD; {объявляем объект класса}BeginB.Init(3.5,5J,2,7); {конструируем объект В}WriteLn(*nnoujadb^ \ B.Square:6:2);{выведет «11лощадь= 94.64»}рА:=@В;{присваиваем указателю базового класса адрес объектапроизводного класса}WriteLn(FInouiadb= \ рА^.Square:6:2); {выведет «Площадь= 94.64»}End,Пример 11.7.
Разработать классы для реализации объекта «Комната Д»,который должен отвечать на запрос о площади, и объекта «Трехмерная комната Д», который должен отвечать на запрос о площади стен и потолка. Пре342//. Иерархии классовдусмотреть внешнюю процедуру вывода результатов, которая получаетадрес объекта через параметр-переменную.Если при разработке классов не объявить метод Square виртуальным полиморфным, то из процедуры Print и для аргумента - объекта базового класса, и для аргумента - объекта производного класса будет вызываться методSquare базового класса.Program case3;Uses RoomMod;Procedure Print(Var R:TRoomD); {процедура с полиморфным объектом}Begin}¥гие1п('Плогцадь='\ R.Square:6:2);End;Var A.'TRoomD; B:TVRoomD; {объявляем объекты-переменные}BeginA.Init(3,5,5J);{конструируем объект A}B.Init(3.5,5.1,2.7); {конструируем объект В}Print(A);{выведет «Площадь= 17.85»)Print(B);{выведет «Площадь= 94.64»}EndПри необходимости во всех трех случаях использования сложного полиморфизма можно определить конкретный тип полиморфного объекта, используя специальную функцию:ТуреО/(<имя класса или о&ъ^ктд>):pointer - возвращает адрес ТВМкласса.
Если адреса ТВМ объекта и класса совпадают, то объект является переменной данного класса. Например:ifTypeOf(SelJ) = ТуреО/(<\\ыя класса>;then <объект принадлежит классу>else <объект не принадлежит классу>При выборе механизма переопределения методов (с ранним или поздним связыванием) в тех случаях, когда это не вызвано необходимостью, следует помнить, что:1) позднее связывание требует построения ТВМ, а следовательно больше памяти;2) вызов виртуальных полиморфных методов происходит через ТВМ, аследовательно медленнее;3) список параметров одноименных виртуальных полиморфных методовдолжен совпадать, а статических полиморфных - не обязательно;4) статический полиморфный метод не может переопределить виртуальный полиморфный метод.343Часть 2. Объектно-ориентированное программированиеОперации присваивания полиморфных объектов.
При выполненииоперации присваивания для полиморфных объектов следует помнить, чтообъект, которому присваивается значение, должен быть сконструирован, т.е.для него должен быть вызван конструктор. Если объект сконструирован небыл, то операция присваивания выполняется некорректно: поля объекта просто обнуляются, и никаких сообщений об ошибке не выдается. Программаже в этом случае естественно работает неправильно.Задания для самопроверкиЗадание 1.
Реализуйте классы, диаграмма которых изображена на рис. 9.7. Разработайте тестирующую программу.Задание 2. Спроектируйте классы для реализации объектов «Меню функций»,«Меню операций» и «Функции» из примера 9.1. Используя объекты этих классов иобъекты классов, разработанных в задании 1, реализуйте программу, функционирующую в соответствии с заданием примера 9.1.11.6. Практикум.
Использование полиморфизмапри создании движущихся изображенийПолиморфное наследование ~ очень мощное средство разработки классов. Переопределение методов при наследовании позволяет изменить поведение объектов, наследуя классы для их реализации от уже разработанных.При создании движущихся изображений полиморфизм позволяет определятьразные законы движения объектов родственных классов.Пример 11.8. Разработать программу, реализующую на экране движение символов по заданным траекториям: по горизонтали слева направо, повертикали сверху вниз и по окружности, размещенной в центре экрана.Объектная декомпозиция предметной области программы изображенана рис.
11.17.ОсновнаяпрограммаПерерисоватьПерерисоватьПерерисоватьСимвол,движущийсягоризонтальноСимвол,движущийсявертикальноСимвол,движущийсяпо окружностиРис. 11.17. Объектная декомпозиция программы«Движение символов»344//. Иерархии классов0,0уt\СX^сX0,0•10,0Хе^Су\Y|Y> 'X:(Ус1сY^" "• 'абвРис. 11.18. Определение законов движения символов:а- при движении погоризонтали;б- при движении по вертикали;в - при движении по окружностиКаждому символу соответствует объект, движение которого происходитпо своему закону (рис. 11.18).
Инициализирует объекты и управляет их движением, посылая сообщение Перерисовать, основная программа, которая наданной декомпозиции представлена в виде объекта.Классы для реализации объектов будут иметь много общего, различаясьтолько полями, определяющими траекторию движения, и методом, реализующим закон изменения положения объекта. В принципе они могут наследоваться один от другого, переопределяя метод Rel, конкретно реализующийзакон пересчета координат для каждого вида движения (рис. 11.19, а). Однако в этом случае объекты будут включать лишние поля (объект классаTVLChar - поле хп и т.д.), что нежелательно.В таких случаях целесообразно использовать абстрактный класс, реализующий некое обобщенное представление объекта, как символа, движуTLineCharch, X, у, хпMove(), Rel(), Init()TCharch, X, уMove(), Rel(), Inlt()АTVLCharynRel(),lnit()ATCirCharxc, yc, r, toRel(),Init()TLineCharxnRel(), Init()TVLCharynRel(), Init()TCirCharxc, yc, r, toRel(),Init()Рис.
11.19. Два варианта иерархии классов:а - обычная; б - с абстрактным классом345Часть 2. Объектно-ориентированное программированиещегося по экрану (рис. 11.19, б). Конкретный закон изменения координат вданном классе определять не будем, но, поскольку соответствующий методRel будет вызываться из метода Move, реализующего движение, определимметод Rel пустым {абстрактным). Классы, наследуемые от абстрактного,должны переопределять этот метод, задавая свои законы изменения координат.
Ниже приведен текст программы.Program ex;Uses crt,Graph;{описание абстрактного класса}Туре TChar=objectch:char; {символ}х,у:integer; {исходное положение}constructor Imt(ach:char;ax, ay: integer);procedure Move(t:integer);procedure Rel(t:integer); virtual;End;Constructor TChar.Init;Beginch:='ach;x:=ax;y:=ay;End;Procedure TChar.Rel;Begin End;Procedure TChar.Move;BeginSetColor(GetBkColor);OuttextXY(x,ych);Rel(t); {вызываем переопределяемый метод изменения координат}SetColor(ord(ch) mod 16);OutTextXY(x,ych);End;{описание класса символа, перемещающегося по горизонтали}Туре TLineChar=object(TChar)xn:integer; {точка отсчета координат по горизонтали}constructor Init(ach:char;aXy ay: integer);procedure Rel(t:integer); virtual;End;Constructor TLineChar.Init;Begininherited Initfach, ax, ay); xn: =ax;End;346//. Иерархии классовProcedure TLineChanRel;Beginx:=(xn+t) mod GetMaxX;End;{описание класса символа, перемещающегося по вертикали}Туре TVLineChar=object(TChar)yn:integer; {точка отсчета координат по вертикали}constructor Init(ach:char;ax, ay: integer);procedure Rel(t:integer); virtual;End;Constructor TVLineCharlnit;Begininherited Initfach, ax, ay);yn:=ay;End;Procedure TVLineChanRel;Beginy:=(yn+t) mod GetMaxY;End;{описание класса символа, перемещающегося по окружности}Туре TCirChar=object(TChar)хс,ус,г:integer; {параметры окружности}tO:real;{исходное положение - начальный угол}constructor Init(ach:char;axc,ayc,ar:integer;atO:real);procedure Rel(t:integer); virtual;End;Constructor TCirChar.Init;BegininheritedInit(ach,axc+round(ar*sin(atO)), ayc+round(ar*cos(atO)));xc:=axc;yc:=ayc;r:=ar;tO:=atO;End;Procedure TCirCharRel;Beginx:=xc+Round(r*sin(tO-^t*0.05));y: =yc-bRound(r *cos(tO-^t *0.05));End;{объявление переменных}Var A:TLineChar;B:TVLineChar;C:TCirChar;347Часть 2.
Объектно-ориентированное программированиеt: integer;i: integer;dn md: integer;{основная программа}Begindr:=detect;InitGraph(dnmd, Ъ:\ВР\ВОГ);AJnitCa'A25);B.InitCb\ 100,0);CJnit('c \GetMaxXdiv 2,GetMaxY div 2,80,0);t:=0;{условное время движения}while not Keypressed and (t< 1000) dobeginA.Move(t); {перерисовываем символы}B,Move(t);C.Move(t);t:=t+l; {увеличиваем условное время движения}for i;=l to 1000 do delay(lOOO); {фиксируем кадр}end;CloseGraph;End.Задания для самопроверкиЗадание 1. Разработайте программу, содержащую описание трех фафическихобъектов: отрезка, правильного треугольника и квадрата.