Основы программирования (947332), страница 56
Текст из файла (страница 56)
Тип объекта известен уже на этапе компиляции программы: он совпадает с типом переменной-объекта, следовательно, и метод,вызываемый в программе, можно определить на этапе компиляции (раннеесвязывание). Данный вариант переопределения методов получил названиепростого полиморфизма, а сами методы были названы статическими полиморфными.Списки параметров статических полиморфных методов могут различаться. Так, в рассмотренном выше примере метод Init класса TRoom имеетдва параметра, а метод Init класса TVRoom2 - три параметра.Обращение объекта производного класса к переопределенному методу базового класса из программы.
Объект производного класса можетобратиться к переопределенному методу базового класса из программы, нодля этого необходимо явно переопределить тип объекта, используя имя базового класса как функцию:<имя базового класса>(<имя объекта производного класса>).<имя метода>.335Часть 2. Объектно-ориентированноепрограммированиеНапример:VarA: TVRoom2;,.,TRoom(A),Square;..{вызываем метод базового класса}Такое переопределение типа в ООП нгзы^^гют восходящим приведениемтипа в отличие от нисходящего^ которое используется, если требуемые методы или поля производного класса при обращении к объекту того же классачерез указатель базового класса не видны (рис. 11.4). Восходящее приведение типа возможно всегда, в то время как при выполнении нисходящего приведения необходимо быть уверенным, что в данный момент времени указатель действительно содержит адрес объекта производного класса или его потомков.11.5.
Сложный полиморфизм. КонструкторыСуществуют три случая, в которых определение типа объекта на этапекомпиляции программы невозможно, и, следовательно, невозможно правильное подключение переопределенного метода. Рассмотрим один из такихслучаев.Пример 11.5, Разработать классы для реализации объекта Комната П,который должен отвечать на запрос о площади пола, выводя результат сразуна экран, и объекта Трехмерная комната П, который должен отвечать на запрос о площади стен и потолка, также выводя результат на экран (рис. 11.11).Класс TRoom2 строим аналогично классу TRoom, добавив метод вывода результата на экран Print. Класс TVRoomP наследуем от TRoomP, переопределив метод определения площади и метод инициализации полей объекта (рис.
11.11).В результате классы будут описаны следующим образом.Площадь?ГКомната ПTRoomPlength, widthSquare(), Init(), Print()Трехмернаякомната ПTVRoomPheightSquare(), Init()Lс7Площадь?L_ .^Рис. 11.11. Объекты:Комната П {а) иТрехмерная комната П (б)336Рис. 11.12. Иерархияклассов дляTVRoomP//. Иерархии классовВ а р и а н т 1 - е ошибкой!Program ex;Type TRoomP=objectlength, width:real;{поля: длина и ширина комнаты}/unction Square:real; {метод определения площади}procedure Print;{мотод вывода результата на экран}procedure /лОДн'.т^яО;{инициализирующий метод}end;Function TRoomPSquare; {метод определения площади}BeginSquare: = length * width;End;Procedure TRoomPPrint; {метод вывода результатов}BeginWriteLnCIInouiadb =', Square:6:2); {внутренний вызов метода}End;Procedure TRoomPJnit; {тело инициализирующего метода}Beginlength: ="1;width: ^^w;End;Type TVRoomP = object(TRoomP)height:real;{дополнительное поле класса}function Square:real; {переопределенный метод класса}procedure Init(l,w,h:real); {переопределенныйинициализирующий метод}end;Procedure TVRoomPInit;BeginшЛт/еб//«/7(7,wj;{инициализирует поля базового класса}height: =h;{инициализируем собственное поле класса}End;Function TVRoomPSquare;Begin {обращаемся к методу базового класса}Square :=inherited Square + 2*height*(length + width);End;Var A:TRoomP; B:TVRoomP; {объявляем объекты-переменные}BeginA.Init(3.5,5,1);{инициализируем поля объекта A}A.Print;{выведет «Площадь = 17.85»}B.Init(3,5,5,1,2.7); {инициализируем поля объекта В}B.Print;{выведет «Площадь = 17.85» - ошибка!!!}End337Часть 2.
Объектно-ориентированноепрограммированиеКласс TRoomPКласс TVRoomPМетод PrintМетод PrintНаследуетсяIПереопределяется^1Метод Square-*мМетод Square1 1- Раннее связываниеПозднее связываниеРис, 11.13. Необходимость позднего связыванияОшибка возникла из-за того, что метод Print, который наследуется классом TVRoomP, вызывает метод Square.
Метод Square в производном классепереопределяется, но метод Print ничего об этом не «знает» и по-прежнемувызывает метод Square класса TRoomP (см. пунктирные стрелки на рис.11.13).Для того чтобы метод базового класса мог в зависимости от типа объекта, для которого он вызван, обращаться либо к методу базового класса, либок переопределенному методу производного класса, необходимо тип объектаопределять на этапе выполнения программы (позднее связывание). Переопределение методов в этом случае называют слоэюным полиморфизмом, асоответствующие методы ~ виртуальными полиморфными.Для организации сложного полиморфизма необходимо:1) переопределяемые методы описать служебным словом virtual;2) к методам класса с виртуальными полиморфными методами добавитьспециальный метод-процедуру - конструктор, в котором служебное словоprocedure заменено служебным словом constructor;3) вызвать конструктор прежде, чем произойдет первое обращение квиртуальным полиморфным методам.Методы, объявленные виртуальными полиморфными, на этапе компиляции подключаться не будут.
Для каждого класса, содержащего виртуальныеполиморфные методы, будет построена специальная внутренняя таблицавиртуальных методов (ТВМ), в которой будут записаны адреса виртуальныхполиморфных методов. Этой таблицей пользуются все объекты данногокласса для определения адресов виртуальных полиморфных методов на этапе выполнения программы.Адрес ТВМ хранится в объекте в специальном внутреннем, невидимомдля программиста поле размером 2 байта (рис. 11.14).
Запись адреса ТВМ вэто поле происходит неявно при выполнении конструктора, поэтому попытки вызовов виртуальных полиморфных методов до выполнения конструктора приводят к ошибкам нарушения адресации и «зависанию» компьютера.338IL Иерархии классовПрограммаТВМ классаОбъект^Дополнительноеневидимое полеобъектаАдресавиртуальныхметодов»Рис.
11.14. Связь объекта с ТВМКак правило, в качестве конструктора используют метод инициализации полей, так как его обычно вызывают в начале работы с объектом. Приповторном вызове конструктора никаких дополнительных действий не выполняется, метод работает как обычная процедура.Пример 11.5. Продолжение. Исправим ошибку предыдущего вариантапрограммы, объявив метод Square виртуальным полиморфным и используяметод Init в качестве конструктора.В а р и а н т 2-правильныйProgram easel;Type TRoomP-objectlength, width.real;{пояя: длина и ширина комнаты}junction Square:real; virtual; {метод определения площади}procedure Print;{метод вывода результата на экран}constructor Init(lyW:real); {конструктор}end;Function TRoomPSquare; {тело метода определения площади}BeginSquare: = length * width;End;Procedure TRoomPPrint; {тело метода вывода результатов}BeginWriteLnf'Площадь =', Square:6:2); {теперь вызов методапроисходит через ТВМ класса }End;Constructor TRoomPInit;{тело конструктора}Beginlength: "="1;width: =w;End;339Часть 2.
Объектно-ориентированное программированиеТуре TVRoomP = object(TRoomP)height:real;{дополнительное поле класса}function Square:real; virtual; {виртуальный полиморфный метод}constructor Init(lyWyh:real); {конструктор}end;Constructor TVRoomP.Init;BeginшЛт/е(^/«//(7,M^^;{инициализируем поля базового класса}height: =h; {инициализируем собстренное поле класса}End:Function TVRoomP Square;BeginSquare:=inherited Square+2'^height*(length+ width);End;Var A:TRoomP; B:TVRoomP; {объявляем объекты-переменные}BeginA.Init(3,5,5J); {конструируем объект A}A.Print; {выведет «Площадь = 17.85»}B.Init(3.5,5.],2,7); {конструируем объект В}B.Print; {выведет «Площадь = 94.64» - верно!!!}End,Определены три случая, когда использование позднего связывания обязательно:1) наследуемый метод для объекта производного класса вызывает метод,переопределенный в производном классе - пример такой ситуации рассмотрен выше;2) объект производного класса через указатель базового класса обращается к методу, переопределенному производным классом;3) процедура вызывает переопределенный метод для объекта производного класса, переданного в процедуру через параметр-переменную, описанный как объект базового класса (данную ситуацию часто называют «процедурой с полиморфным объектом»).Примечание.
При использовании параметров-значений аргумент-объект копируется.При этом копируется объект типа параметра, т.е. базового. Следовательно, передать в процедуру объект производного класса не удастся.Собственно, все три случая сводятся к одному: обращение к полиморфному методу выполняется через указатель базового класса, которому можетбыть присвоен адрес объекта не только базового, но и производного класса.В теории программирования объект, адресуемый подобным указателем, получил название полиморфного,34011.
Иерархии классовРассмотрим более подробно второй и третий случаи и покажем, что действительно ис Площадь?Комната Дпользование сложного полиморфизма применительно к ним обязательно.Пример 11.6. Разработать классы для реализации двух динамических объектов: объектКомната Д должен отвечать на запрос о площади, объект Трехмерная комната Д должен отвеПлощадь?Трехмернаячать на запрос о площади стен и потолка (рис.комната Д11.15). Предусмотреть возмоэюиость обращения к полям и методам производного классачерез указатель на базовый класс.Между классами прослеживается отношеРис.