Г. Шилдт - Полный справочник по C++ (1109478), страница 68
Текст из файла (страница 68)
йегьчей()пе х, Ьпе у): Ьяяе(у) ( З=х: сонг « "Создание объекта класса йег1чей1п"г Глава 16. Наследование -йек?чей() ( соне «Уничтожение обьекта класса йек?чей)п"; чоай внон() ( соос «1 « " " «З « "1п"; ?пе гаа?п() ( йег1чей оЬ(З, 4) оЬ.вноы(); // Выводит на экран числа 4 3 кесокп С; ) Здесь конструктор класса йек1чей имеет два параметра: ж и у. Однако в самом ко??с?рук?бра используется лишь переменная ж, а переменная у передается конструктору базового класса. Как правило, в конструкторе п)юизводного класса должны объявляться все параметры, необходимые базовому классу. Для этого они указываются после двоеточия в списке аргументов конструктора базового класса. Рассмотрим пример множественного наследования. $?пс1нйе <?овегеат> чв?пд патеврасе вой; с1авв Ьаве1 рсосессей: апе роЬ?(с: Ьаве1(апе х) ( 1-.х; соьс « "Создание объекта класса Ьаве11п"/ -Ьаве1() ( соне « "Уничтожение объекта класса Ьаве1М"? ) ); с1авв Ьаве2 ( рсосессей: УЬС )г; роЫ?с: Ьаве2(?пе х) ( )г=х/ соне « "Создание объекта класса Ьаве21п"; -Ьаве2() ( соос « "Уничтожение объекта класса Ьаве21п"; ] ); с1авв йек?чей: риЫ?с Ьаве1, роЫ?с Ьаве2 ( 1пс 3; роЫ1с: йег(чей(1пс х, ъпс у, 1пс з): Ьаве1(у), Ьаве2(з) ( З=х; соне « "Создание объекта класса йек?чей1п"; ) -йек?чей() ( соос « "Уничтожение объекта клаесса йек1чей1п"; чогй знои() ( свое « 1 « " " « з « " " « )с « ")и"; ) ]; 1пс иа?п() ( йетвчей оЬ(3, 4, 5); оЬ.эпок()? // выводит на экран числа 4 Э 5 кесьоп О; Часть Н.
Язык С++ Подчеркнем, что аргументы конструктора базового класса передаются с помоигью аргументов конструктора производного класса. Следователыю, даже если конструктор производного класса не имеет собственных аргументов, его объявление должно содержать аргументы конструкторов базовых классов. В этом случае аргументы, передаваемые конструктору производного класса, просто переправляются конструкторам базовых классов. Например, в рассмотренной ниже программе конструктор класса Сехочвб не имеет аобстненных аргументов, а конструкторы класса Ьаяе1 и Ьаве2, напротив, имс|от по одному параметру. Еьпс1иде <ьоягееаи> ияьпп паюеяраае яСС(; а1аяя Ьаяе1 ртоеесеебьпе риЬ11а: Ъаяе1(ьпе х) ( ь=х; апис « "Создание объекта клааса Ьаяе11п'; ) -Ьаяе1() ( аоие « "Уничтожение объекта класаа Ьаяе11п"; ) с1аяя Ьаяе2 ( ргосессе<): 1пс ры риЫ ьа: Ьаяе2(ьпе х) ( )г=х; аоис « "Создание объекта класса Ьаяе21п"; ) -ьаяе2() ( апис « "уничтожение объекта класаа ьаяе21п"; ) а1аяя дегьчеб: риЫьа Ъаяе1, риЫьс Ьаяе2 ( риЫза: /* Конструктор клааса г)ег1чеб не имеет параметров.
в его объявлении указываются параметры конатрукторав базовых клаааов. йегьчеб(1пе х, 1пе у): Ьаяе1(х), Ьаяе2(у) ( аоие « "Создание объекта класса г)егьчес)1п"; ) -бетьчеб() ( аоие « "Уничтожение объекта класса г)екьчеФп"/ ча16 БЬОы() ( ааие « 1 « " « К « "'и"; ьпс юа1п! ) ( бегьчеб оЬ(З, 4) оЬ.япоы(); // Выводит на экран числа 3 4 гееи*п 0; ) Конструктор производного класса может произвольно использовать все параметры, указанные в его объявлении, даже если они передаются конструкторам базового класаа. Иначе говоря, передача параметров конструкторам базовых классов не исключает их использования внутри произаодного класса.
Таким образом, фрагмент программы, приведенный ниже, является абаолютно правильным. а1аяя дегьчеб: риЬ)ьа Ьаяе ( зпе 1; Глава 16. Наследование роЫхс. // Класс йесъчей использует оба параметра х и у, // а затем передает их конструктору базового класса.
йесьуей(1пс х, ъпс у): Ьаее(х, у) ( З = х*у; созе « "Оседание объекта класса йегъуейМ"; Передавая параметры конструкторам базовых классов, следует иметь в виду, что в качестве аргумента могут использоваться любые допустимые выражения, например, вызовы функций или переменные. Это полностью согласуется с принципом динамической инициализации объектов, предусмотренной в языке С++.
~ 1 Предоставление доступа Если к базовому классу применяется механизм закрытого наследования, все его огкрытью и зашишенные члены становятся закрытыми членами производного класса. Однако в некоторых ситуациях можно восстановить исходныи статус одного или нескольких унаследованных членов, которые ранее были открытыми или зашишенными. Например, может аозникнуп необхолимость сохранить открытый доступ к некоторым членам базового класса, несмотря на то что они наследуются закрытым произволным классом.
Станларт языка С++ предусматривает лля этого два пути. Во-первых, можно применить оператор цв1пп. Этот способ более предпочтителен. Оператор цвхпд предназначен для поддержки пространств имен и обсуждается в главе 23. Во-вторых, можно использовать объявление уровня с)астуяа (ассеы йес!агайоп) в производном классе. Этот способ также поллерживается стандартом языка С++, однако считается нежелательным.
Это значит, чзо в новых программах его следует избегать. Однако, поскольку в обиходе остается большое количеспю старых программ, рассмотрям этот пример подробнее, Объявление уровня доступа выглядит следующим образом. й базовый класс:: член; Это объявление размешается внутри производного класса после заголовка соответствуюшего раздела. Обратите внимание на то, что тип переменной в объявлении уровня доступа указывать не следует. с1азз Ьазе ( роЫ1с: дпг З; // Открытый член класса Ьаве ): // Закрытый наследник класса Ьазе.
с1аьз йегъуей: ргвуасе Ьаае ( роЫТс: // Место дяя объявления уровня доступа. Ьаае::З; // Теперь переменная 3 снова открыта. Поскольку класс йех1чей является закрьпым наследником класса Ьаве, открытая переменная-член 5 из класса ьеве становится закрьпой переменной-членом класса йехдчей. Однако в раздел роЫхе класса йехйчгеа мы поместили объявление уровня доступа Я Ьазе::3; Переменная 5 снова стала открытой.
Часть й. Язык С++ Этот способ можно применять для восстановления статуса открытых и защищенных членов. Однако с его помощью нельзя поднять или понизить уровень доступа к члену класса. Например, член„объявленный закрыуым в базовом классе, нельзя сделать открытым в производном. (Если бы это было вбзможно, механизм инкапсуляции был бы полностью разрушен!) Следующая программа иллюстрирует обьявление уровня доступа. Обратите внимание на то, как восстанавливается открытый статус членов 5, ееех () и йеех() .
))1пс1цпе <1овххеащ> ивхпп пащеврасе вес): с1авв Ьаве хпе х; // Закрытый член класса Ьаве рцЬ1хс: к; чоЫ вее1(1пе х) ( 1 - "к; ) 1пс дех1() ( хехцхп х; ) ); // Закрытый наследник класса Ьаве. с1авв Неххуебн рхьчаее Ьане ( риЬ11с: /* Следующие три оператора восстанавливают открытый статус членов 3, вехй() и Нех1 О . */ Ьаве::З; // Переменная 1 снова открыта, а переменная Х вЂ” нет. Ьаве::вес1; // Функция вее1() снова открыта.
Ьаве::сее1; // Функция дех1() снова открыта. // Ьаве::х; // Неверно, уровень доступа поднимать нельзя. 1пс а; // Открытый член. ): 1пх ща1пО ( с)ех1чее) оЬ; //оЬ.х = 10; // Нельзя, так как переменная з. // является закрытым членом класса е)ех1уее). оЬ.З = 20; // Кожно„ так как переменная З открыта в классе с)еххуес(. //оЬ.Х = 30; // Нельзя, поскольку переменная к является // закрытым членом класса е)еххчег).
оЬ.а = 40; // Кожно, так как переменная а является открытым // членом класса е)еххчес). оЬ. вее1 (10); соцс « оЬ.дес1 О « " " « оЬ.1 « " " « оЬ.а: хеснхп 0; Этот способ позволяет восстанавливать уровень доступа к некоторым открытым или защищенным членам класса, оставляя произвпдный класс закрытым. Глава 16. Наследование йФ Несмотря нв то чпю язых С++ допусхавгп объявления уровня даолупв, они счьглв- ЩЩ ются нвжвпвгпвпьнымп. Эпю озечает, чпю прп ссидвнгю новых программ ечвапа ~ ннх следует применять опврвгпор ивХав„одеспвчоваюи(ий гпогп жв рвзупьгпвт.
Виртуальные базовые классы При множественном наследовании может возникнуп неоднозначность. Рассмотрим, например, следующую неправильную программу. // Зта программа содержит ошибку и не компилируется. $(пс1ийе <1овсгеаш> ие(пц пашеврасе впйг с1авв Ьаве ( риЫьсг (пг 1; // Класс йегачей1 является наследником класса Ьаве. с1авв йег(чей1: риЬ11с Ьаве ( риЫас: 1пс Зг // Класс йеггчей2 является наследником класса Ьаве. с1авв йег(чей2: риЬ11с Ьаве ( риЬ11сг (пе )с; ); /* Класс йег(чейЗ является наследником классов йегьчей1 и йегачей2. Следовательно, в классе йегьчейЗ существуют две копии класса Ьаве) */ с1авв йег(чейЗ: риЫ(с йег1чей1, риЫ)с йег1чей2 риЫ(с г ).пс виси )г (пе ша(п ( ) йег(чейЗ оЬ; оЬ.( = 10г // Неоднозначность, какая переменная 1 имеется в виду??? оЬ.З = 20г оЬ.)г = Зсг Здесь переменная 1 также определена неоднозначно.
оЬ.виш = оЬ.1 + оЬ.З + оЬ.КЧ // Здесь переменная 1 также определена неоднозначно. свис « оЬ.1 « сома « оЬ.З « " " « оЬ.)с « сонг « оЬ.вгпп," гееигп 0; Часть )!. Яамк С++ Как указано в комлгентариях, классы йетачей1 и йетхчей2 являются наследниками класса ьаве. Однако класс йетаьейз является производным от обоих классов йетъчейа и йетъчейз. (Такое наследование называется бриллианязоаым. — Прим. ред.) Следовательно, в объекте класса йетхчейз содержатся две копии объекта класса ьаве. Таким образом, выражение Ц оЬ.1 = 20;, в котором происходи~ обращение к переменной 1, становится неоднозначным. поскольку неизвестно, из объекта какого класса следует взять эту переменную: йетзлгей1 или йетачейа.
Обладая двумя копиями объекта класса Ьаве, обьект класса оЬ СОДЕРжит два экземпляра переменной оЬ. Ы Как видим, этот оператор в принципе неоднозначен. Эту программу можно исправить двумя способами. Во-первых, к переменной 1 можно применить оператор разрешения области видимости. Например, слсдуюшая программа работает совершенно правильно. // Б этой программе для явногс выбора переменной 1 // применяется опера ор разрелзения области видимости. Фъпс1пйе <(овсгеаяз> ивъпд памеврасе вгйз с1авв Ьаве ( риЬ1(сз ъпе // Класс йег1чей1 является наследником класса Ьаве.