Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 64
Текст из файла (страница 64)
Когда в производном классе указывается ключевое слово Ьазе, вызывается конструктор из его непосредственного базового класса. Следовательно, ключевое слово Ьазе всегда обращается к базовому классу, стоящему в иерархии непосредственно иэд вызывающим классом. Это справедливо даже для многоуровневой иерархии классов. Аргументы передаются базовому конструктору в качестве аргументов метода Ьазе () . Если же ключевое слово отсутствует, то автоматически вызывается конструктор, используемый в базовом классе по умолчанию. Наследование и сокрытие имен В производйом классе можно определить член с таким же именем, как и у члена его базового класса.
В этом случае член базового класса скрывается в производном классе. И хотя формально в С№ это ие считается ошибкой, компилятор все же выдаст сообщение, предупреждающее о том, что имя скрывается. Если член базового класса требует- 330 Часть!. Язык С№ ся скрыть намеренно, то перед его именем следует указать ключевое слово пеи, чтобы избежать появления подобного предупреждающего сообщения. Следует, однако, иметь в виду, что это совершенно отдельное применение ключевого слова пеи, не похожее на его применение при создании экземпляра объекта. Ниже приведен пример сокрытия имени.
// Пример сокрытия имени с наследственной связью. оаьпч Вуакеи) с1ааа А ( рпЫ№с 1ЛС 1 = 0; ) // Создать производный класс. с1ааа В: А ( пеи 1пс 1; О этот член скрывает член 1 из класса А риЫ№с В(ьпк Ы ( 1 = Ьт // член 1 в классе В ) рпЬ11с тома зпон() ( Сопао1е.иггсеъгпе("Член 1 в производном классе: " + 1); ) с1аэа Наиен1к(1ЛЧ ( зкак1с чо1С Маги() ( В оЬ = пен В(2)т ОЬ.
ЭЛОн () ) ) Прежде всего обратите внимание на использование ключевого слова пен в следующей строке кода: пен гпк 1; // этот член скрывает член 1 из класса А В этой строке, по существу, компилятору сообщается о том, что вновь создаваемая переменная 1 намеренно скрывает переменную 1 из базового класса А и что автору программы об этом известно. Если же опустить ключевое слово пен в этой строке кода, то компилятор выдаст предупреждающее сообщение. Вот к какому результату приводит выполнение приведенного выше кода Член 1 в производном классе: 2 В классе В определяется собственная переменная экземпляра 1, которая скрывает переменную 1 из базового класса А. Поэтому при вызове метода ВЬон() для объекта типа В выводится значение переменной 1, определенной в классе В, а не той, что определена в классе А.
Глава )1, )(асаедование 331 Применение ключевого слова Ьаее длв доступа к скрытому имени Имеется еще одна форма ключевого слова Ьазе, которая действует подобно ключевому слову сь15, эа исключением того, что оиа всегда ссылается иа базовый класс в том производном классе, в котором оиа используется. Ниже эта форма приведена в общем виде. Ьаве. член где член может обозначать метод или переменную экземпляра. Эта форма ключевого слова Ьазе чаще всего применяется в тех случаях, когда под именами членов производного класса скрываются члены базового класса с теми же самыми именами.
В качестве примера ниже приведен другой вариант иерархии классов иэ предыдущего примера. // Применение ключевого слова Ьазе для преодоления // препятствия, связанного с сокрытием имен. пвьпч 5увпею; с1аээ А ( рпвьтс 1пп 1 = Оу ) // Создать производный класс. с1авз В: А ( пен ьпп 1; // этот член скрывает член 1 из класса А рпЬ11с В(1пп а, 1пп Ь) ( Ьаэе.1 = а) // здесь обнаруживается скрытый член иэ класса А 1 = Ь; // член 1 в классе В ) рпвтьс чоха Зпон() // Здесь выводится член 1 из класса й. Сопзо1е.вгьпепхпе("Член 1 в базовом классе: " ь Ьаэе.ь); // й здесь выводится член 1 из класса В.
сопзо1е.иг1сеь1пе("член 1 в производном классе: " + 1)) ) ) с1азв Ппсонегпаюе ( эпапьс чо1б Маьп() ( В оЬ = пен В(1, 2)," оЬ.5ьок(); ) 1 Выполнение этого кода приводит к следующему результатУ: Член 1 в базовом классе: 1 Член 1 в производном классе: 2 332 Часть (. йзык С№ Несмотря иа то что переменная экземпляра 1 в производном классе В скрывает перемеииую 1 из базового класса л, ключевое слово ьаяе разрешает доступ к переменной т., определенной в базовом классе.
С помощью ключевого слова ьа я е могут также вызываться скрытые методы. Например, в приведенном ниже коде класс В наследует класс д и в обоих классах объявляется метод 5Ьон () . А затем в методе 5Ьоы () класса В с помощью ключевого слова Ьаяе вызывается вариант метода БЬон (), определенный в классе л. // Вызвать скрытый метод. пя1пц Яуякеют с1аяя а ( рпвтас 1пс 1 = От // метод Бьон() в классе а роЬ11с чоао Бпон() ( Сопяо1е.нгккеь1пе("Член 1 в базовом классе: " ь 1); ) // Создать проиэводньп» класс. с1аяя В: а ( пен апк 1; // этот член скрывает член 1 из класса й рпв11с В(1пк а, апк Ь) ( Ьаяе.1 = а; // здесь обнаруживается скрытый член из класса Л 1 = Ьт // член 1 в классе В ) // Здесь скрывается метод ЯЬон() из класса Л.
Обратите // внимание на применение ключевого слова пен. пен рпЬ11с чояб БЬон() ( Ьаяе.БЬон()т // здесь вызывается метод ЯЬон() из класса а // далее выводится член 1 из класса В СОПяО1Е.ИГ№СЕ)ЬПЕ(нЧЛЕН 1 З ПрОИЗВОдНОМ Кпаооз: " + 1); ) ) с1аяя Опсочегиаюе ( ясасас чояб паап() ( В оЬ = пен В(1, 2)т оЬ.БЬон()т ) ) Выполнение этого кода приводит к следующему результату: Член 1 з базовом классе: 1 Член 1 в производном классе: 2 Как видите, в выражении Ьаяе.
ЯЬон () вызывается вариант метода БЬом () из базового класса. Глава ((. Наследование 333 Обратите также внимание на следующее: ключевое слово пеы используется в приведенном выше коде с целью сообщить компилятору о том, что метод ЯЬоы (), вновь объявляемый в производном классе В, намеренно скрывает другой метод ЯЬоы (), определенный в базовом классе й. Создание многоуровневой иерархии классов В представленных до сих пор примерах программ использовались простые иерархии классов, состоявшие только из базового и производного классов.
Но в С№ можно также строить иерархии, состоящие из любого числа уровней наследования. Как упоминалось выше, многоуровневая иерархия идеально подходит для использования одного производного класса в качестве базового для другого производного класса. Так, если имеются три класса, й, В и С, то класс С может наследовать от класса В, а тот, в свою очередь, от класса й. В таком случае каждый производный класс наследует характерные особенности всех своих базовых классов.
В частности, класс С наследует все члены классов В и й. Для того чтобы показать, насколько полезной может оказаться многоуровневая иерархия классов, рассмотрим следующий пример программы. В ней производный класс ТТ1апд1е служит в качестве базового для создания другого производного класса— Со1огтг1апд1е. При этом класс Со1огТг1апд1е наследует все характерные особенности, а по существу, члены классов Тг1апд1е и ТыоОЯЬаре, к которым добавляется поле со1ог, содержашее цвет треугольника. // Пример построения многоуровневой иерархии классов.
ивтпд Яуввешв с1авв ТноОЯЬаре ( бонЫе ргл н1бСЬ; бооЫе ргъ Ье1дЬЫ // Конструктор, используемый по уыолчанию. риЬ11с ТноОЯЬаре() ( И1оЕЬ = Нелдпе = О.О; // конструктор для класса тмооэьаре. рнЫъс Тнопэпаре(сооЫе н, бооЫе Ы ( Н1оеп = и; Не1дЬЬ = Ьв // Сконструировать объект равной ширины и высоты. рнЫлс Тнопзпаре(бооЬ1е х) ( И1оЬЬ = Нелдпе = х~ // Свойства ширины и высоты объекта. рнЫТс боиЫе Илбвп ( дев ( гевогп рг1 н1оЬЫ ) 334 Часть !.
язык С» яеС ( рга н16СЛ = ча1ие < О ? -ча1ие: ча1ие) ) ) риЬ11с боиЬ1е НеадЬС ( деС ( геиисп рг1 Ле1дЛСк ) зеС ( рса ЛеадЛС = ча1ие < О ? -ча1ие: ча1ие) ! риЫТс чоаб ЯЛонб1щ() ( Сопяо1е.нг1Сесспе("Ширина и высота равны " + И1бсЛ т " и " + Не1дЛС)) ) ) // Класс для треугольников, производный от класса ТноОБЛаре. с1аяя Тсаапд1е: ТноПЯЛаре ( якс1пд ЯСу1е; // закрытый член класса /* Конструктор, используемый по умолчанию. Автоматически вызывает конструктор, доступный по умолчанию в классе ТноОБЛаре.
*/ риЫТс Тг1апд1е() ( БСу1е = "пи11"к ) // Конструктор. риЫ1с Тгаапд1е( яиг1пд я, боиЬ1е н, боиЬ1е Ы: Ьаяе(н, Ы ( 5Су1е = я) ) // Сконструировать равнобедренный треугольник. риЬ11с Тгаапд1е(боиЫе х) : Ьаяе(х) ( 5Су1е = "равнобедренный"; // Возвратить площадь треугольника. риЬ11с боиЬ1е Асеа() ( сеписп И1ОСЛ * НеадЛС / 2к ) // показать тип треугольника. риЫТс чо1б ЯЛонзсу1е() ( Сопяо1е.нгакеЬТпе("Треугольник " т 5Су1е); // Расширить класс тсаапд1е. с1азя Со1огТсаапд1е: Тсаапд1е ( якг1пд со1ос; риЫ1с Со1огТсаапд1е(якс1пд с, якг1пд я, боиЫе и, боиЫе Ы: Ьаяе(я, н, Ы ( Глава 11.
Наследованне 33$ со1ог = с; ) // Показать свет треугольника. рос11с уогб Явоисо1ог() [ Сопво1е.нг1геьгпе("Цвет " + со1ог); ) с1авв 5нарев6 ( всасгс уо10 Магп() ( Со1огТг1ап01е Г1 = пеи Со1огтг1ап01е("синий", "прямоугольный", 8.0, 12.0)) Со1огтг1апд1е С2 = пеи Со1огтггап01е("красный", "равнобелренный", 2.0, 2.0)8 Сопво1е.иг1геь1пе("Сведения об объекте Г1. ""); С1. ЯпоиЯсу1е (); Г1.5поипьщ()8 С1.5понСо1ог () ) сопво1е.игьгеььпе("площадь равна " + с1.лгеа())~ Сопяо1е.нгьгеьгпе()) Сопво1е.иг1сеььпе("Сведения об объекте Г2: ")) С2.5воизгу1е(); г2.5нонпащ()! Г2.5поиСо1ог()," сопво1е.иг1сепапе("площадь равна " ъ с2.аггее()); ) ) При выполнении этой программы получается следующий результат: Сведения об объекте Г1: Треугольник прямоугольный Ширина и высота равны 8 и 12 Цвет синий Площадь равна 48 Сведения об объекте Г2: треугольник равнобедренный амрина и высота равны 2 и 2 Цвет красный Площадь равна 2 Благодаря наследованию в классе С01огтг1апс1е могут использоваться определенные ранее классы Тг1апо1е и ТиоПБПаре, к элементам которых добавляется лишь та информация, которая требуется для конкретного применения данного класса.
В этом отчасти и состоит ценность наследования, поскольку оно допускает повторное использование кода. 336 Часть ). Язык СЗ Приведенный выше пример демонстрирует еще одно важное положение: ключевое слово Ьаяе всегда обозначает ссылку иа конструктор ближайшего по иерархии базового класса. Так, ключевое слово Ьаяе в классе Со1оттг1апд1е обозиачаег вызов коиструктора из класса тг1апд1е, а ключевое слово Ьаяе в классе Тгтапд1е — вызов конструктора из класса тиопзпаре.
Если же в иерархии классов конструктору базового класса требуются параметры, то все производные классы должны предоставлять эти параметры вверх по иерархии, независимо от того, требуются оии самому производному классу или иет. Порядок вызова конструкторов В связи с изложенными выше в отношении наследования и иерархии классов может возникнуть следующий резонный вопрос: когда создается объект производного класса и какой коиструктор выполняется первым: тот, что определен в производном классе, или же тот, что определейв базовом классе? Так, если имеется базовый класс А и производный класс В, то вызывается ли конструктор класса А раньше конструктора класса В, или же наоборот? Ответ иа этот вопрос состоит в том, что в иерархии классов конструкторы вызываются по порядку выведения классов: от базового к производному.