Г. Шилдт - С#4.0 Полное руководство (1160795), страница 67
Текст из файла (страница 67)
При переопределении имя, возвращаемый тип и сигнатура переопределяющего метода должны быть точно такими же, как и у того виртуального метода, который переопределяется. Кроме того, виртуальный метод не может быть объявлен как я са сьс или а)зя Огас Г (подробнее данный вопрос рассматривается далее в этой главе). Переопределение метода служит основанием для воплощения одного из самых эффективных в С)) принципов: динамическои диспетчеризации методов, которая представляет собой механизм разрешения вызова во время выполнения, а не компиляции.
Значение динамической диспетчеризации методов состоит в том, что именно благодаря ей в С() реализуется динамический полиморфизм. Ниже приведен пример, демонстрирующий виртуальные методы и их переопределение. Продемонстрировать виртуальный метод. пятна Зуясекч с1авя Ваяв Сознать виртуальный метод в базовом классе. рпЫ1с ч1гспа1 чогб Х)зо() ( Сопяо1е.нг1сеььпе("Метод Хбо() в классе Вазе" ) ) ) с1аяя Оег1чеб1 : Ваяв ( Переопределить метод Хьо() в производном классе. роЫ1с очегг1бе чо1С Х)зо() ( Сопяо1е.нгьгеььпе("Метод Х)зо() в классе Оеггчеб1") ) с1авя Оеггчеб2 : Ва*е ( // Вновь переопределить метод Хпо() в еле одном проияводном классе.
рпЫ1с очеггьбе чоьб Хпо() ( Сопяо1е.Хгьсеььпе("Метод Х)зо() в классе Оегьчеб2"); ) ) Глава 11. Наследование 357 с1аяя Онегггбебеыо ( ягаггс чогб ма1п() ( Ваяв ЬаяеОЬ = пен Ваяе()г Оег1чеб1 бОЬ1 = пеи Оеггчеб1() Оегкнеб2 бОЬ2 = пен Оеггчеб2() Вазе Ьаяерет; // ссылка на базовый класс Ьаяейеб = ЬаяеОЬ| Ьаяейес.ХЬо()г Ьаяевег = бОЬ1; Ьаяейес.иьо(); Ьаяекес = бОЬ2; Ьаяейет.иьо(); Вот к какому результату приводит выполнение этого кода.
Метод ХЬо() в классе Вазе. Метод Хьо() в классе Оеггчеб1 Метод Хьо() в классе Оеггчеб2 В коде из приведенного выше примера создаются базовый класс Вазе и два производных от него класса — Оеггчеб1 и Оеггчеб2. В классе Вазе объявляется виртуальный метод ХЬо (), который переопределяется в обоих производных классах. Затем в методе Мага () объявляются объекты типа Вазе, Оегбчеб1 и Оеггчеб2. Кроме того, объявляется переменная Ьазейег ссылочного типа Вазе. Далее ссылка на каждый тип объекта присваивается переменной Ьазейе г и затем используется для вызова метода ХЬо () . Как следует из результата выполнения приведенного выше кода, вариант выполняемого метода ХЬО () определяется по типу объекта, к которому происходит обращение по ссылке во время вызова этого метода, а не по типу класса переменной ЬазейеГ.
Но переопределять виртуальный метод совсем не обязательно. Ведь если в производном классе не предоставляется собственный вариант виртуального метода, то используется его вариант из базового класса, как в приведенном ниже примере. !* Если виртуальный метод не переопределяется, то используется его вариант из базового клааса. */ оягп9 яуясекы с1аяя Ваяв создать виртуальный метод в базовом классе. роЬ11с нггсоа1 ноьб Хьо() ( Сопяо1е.иггбеъгпе("Метод ХЬо() в классе Ваяе") ) с1аяя Оег1чеб1 : Ваяе ( Переопределить метод Хво() в производном классе.
358 Часть (. Язык С() рпЬ11с очеггдде чодд ХЬо() ( Сопяо1е.нггсеъгпе("Метод ИЬо() в классе Регьчед1") ) ) с1аяя Оегьчед2 : Вазе ( // В этом классе метод ИЬо() не переопределяется. ) с1аяя Иоочегг1деОеио ( ясастс чодд Ма1п() ( Ваяе ЬаяеОЪ = пен Ваяв(); Оегьчед1 дОЬ1 = пен Реггчед1() Оегтчед2 дОЬ2 = пен Оегтчед2() Вазе Ьаяейег; // ссылка на базовый класс ЬаяейеЕ = ЬаяеОЪ; Ьаяеаес. Ино (); Ьаяевег = дОЬ1; ЬаяедеЕ.ХЬо()т Ьаяекег = дОЬ2; ЬаяеаеЕ.ИЬо(); // вызывается метод ИЬо() из класса Ваяе ) ) Выполнение этого кода приводит к следующему результату, Метод ХЬо() в классе Ваяв. Метод ИЬо() в классе Оег1чед1 Метод ИЬо() в классе Вазе В данном примере метод ХЬо () не переопределяется в классе Оегдчед2.
Поэтому для объекта класса Оегьчед2 вызывается метод ХЬо () из класса Вазе. Если при наличии многоуровневой иерархии виртуальный метод не переопределяется в производном классе, то выполняется ближайший его вариант, обнаруживаемый вверх по иерархии, как в приведенном ниже примере. /* В многоуровневой иерархии классов выполняется тот переопределенный вариант виртуального метода, который обнаруживается первым при продвижении вверх по иерархии. */ пядпд Зуясеит с1аяя Вазе ( // создать виртуальный метод в базовом классе.
рпЬ11с чдгбоа1 чоьд ХЬо() ( Сопяо1е.нг1оегипе("Метод ИЬо() в классе Ваяе") ) ) с1аяя Регтчед1 : Ваяе Глава 11. Наследование 359 // Переопределить метод ИЬо() в производном классе. риЬ11с очеттьде чогд ИЬо() ( Сопво1е.иг1пеььпе("Метод ИЬо() в классе Оегьчед1") ) ) с1ааз Оегьчед2 : Оегьчед1 ( // В этом классе метод ИЬо() не переопределяется. с1авв Оегьчедз : Оеттчед2 ( И в этом классе метод ХЬо() не переопределяется.
) с1ааз Иоочеггьдеоеэо2 ( впаг1с чоьд Ма1п() ( Оегьчедз дОЬ = пен Ретьчедз (); Вазе Ьааевет) // ссылка на базовый класс Ьавекет = дОЬ! Ьавекет.ИЬо(); // вызов метода ИЬо() из класса Оегьчед1 Вот к какому результату приводит выполнение этого кода. Метод ИЬо() в классе Оетьчед1 В данном примере класс Рег1чедЗ наследует класс Регьчед2, который наследует класс Регдчед1, а тот, в свою очередь, — класс Вазе. Как показывает приведенный выше результат, выполняется метод ИЬо (), переопределяемый в классе Оег1чед1, поскольку это первый вариант виртуального метода, обнаруживаемый при продвижении вверх по иерархии от классов РегьчедЗ и Оегьчед2, где метод ИЬо () не переопределяется, к классу Регдчед1. И еще одно замечание: свойства также подлежат модификации ключевым словом ч1,гбпа1 и переопределению ключевым словом очеггтде.
Это же относится и к индексаторам. Что дает переопределение методов Благодаря переопределению методов в С() поддерживается динамический полиморфизм. В объектно-ориентированном программировании полиморфизм играет очень важную роль, потому что он позволяет определить в общем классе методы, которые становятся общими для всех производных от него классов, а в производных классах — определить конкретную реализацию некоторых или ясе всех этих методов. Переопределение методов — это еще один способ воплотить в СФ главный принцип полиморфизма: один интерфейс — множество методов.
Удачное применение полиморфизма отчасти зависит от правильного понимания той особенности, что базовые и производные классы образуют иерархию, которая продвигается от меньшей к большей специализации. При надлежащем применении базовый класс предоставляет все необходимые элементы, которые могут использоваться в производном классе непосредственно. А с помощью виртуальных методов в базовом 360 Часть (. Язык С() классе определяются те методы, которые могут быть самостоятельно реализованы в производном классе.
Таким образом, сочетая наследование с виртуальными методами, можно определить в базовом классе общую форму методов, которые будут использоваться во всех его производных классах. Применение виртуальных методов Для того чтобы стали понятнее преимущества виртуальных методов, применим их в классе ТхоРБЬаре. В предыдущих примерах в каждом классе, производном от класса ТхоРБЬаре, определялся метод Агеа () . Но, по-видимому, метод Агеа () лучше было бы сделать виртуальным в классе ТхоРБЬаре и тем самым предоставить возможность переопределить его в каждом производном классе с учетом особенностей расчета площади той двумерной формы, которую инкапсулирует этот класс. Именно это и сделано в приведенном ниже примере программы.
Ради удобства демонстрации классов в этой программе введено также свойство паве в классе ТыоРБЬаре. Применить виртуальные мстоды и полиморфизм. ияьпэ Буягет; с1аяя Тноовиаре ( боиЫе ргь н1НСЬ) г(оиЫе ргг ЬеьСЬС1 Конструктор по умолчанию. риЬ11с ТноРБЬаре() ( Х1НСЬ = Негсис = 0.0; паве = "по11"; Параметрияированный конструктор. рнЬ11с Тноозиаре(бооЬ1е н, НоиЫе Ь, яягьпч п) ( Хгбси = н1 Неьчии = Ь; паве = и; ) Сконструировать объект равной ширины и высоты.
риЫ1с ТноОБЬаре (доиЫе х, яогьпч п) Х1НСЬ = Не1СЬС = х) пате = п; ) Сконструировать копию объекта Тнорзиаре. роЬ11с Тноозиаре(Тноозиаре оь) Хгбии = ОЬ.Х1бсЬ1 НеьНЬС = ои.не1СЬС1 паве = оЬ.пате; ) Свойства ширины и высоты объекта. риЫ1с боиЫе Х1НСЬ ( ОЕС ( ГЕСИГП РГ1 НГНСЬ1 ] Глава 11. Наследование 361 яес ( ргг н1НГЬ = ча1пе < 0 ? -ча1ие: ча1ое) ) рчЫгс НопЫе Не1НЬГ ( Оео ( геснгп рг1 Ье10ЬЫ яег .( рг1 Ье1слг = ча1се < 0 ? -ча1пе : ча1ое; ) ) роЬ11с ясг1пО вате ( цеЫ яеЫ риЫгс чо1б ЯЬонбят() ( Сопяо1е.ыг1ГеЬТпе(тйирина и высота равны " Нгйсл + " и " + Не1сЬГ); ) рпЫТс чягспа1 боиЫе Агеа() сопяо1е.иг1геьяпе("метод Агеа() лолжен быть переопределен" ) гесигп 0.0; ) Класс для треугольников, производный от класса ТноОЯЬаре.