Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Глава28.Множественноенаследование




М-да...               говори "гоп", пока не переехал      — новая иерархия классов со- вершенно  нас  не weight остается неоднозначным. Попробуем привести ss к классу Furniture ?

 


void

{


 

SleeperSofa ss;

pF;


pF =

cout « = "

<< pF -> weight

<<"\n";

};

Приведение ss к классу Furnitur e тоже ничего не дает. Более того, я получил ка-

кое-то подозрительное сообщение о том, что приведение SleeperSofa* к классу Furniture * неоднозначно. Да что, в конце концов, творится?

На самом деле все довольно просто. Класс SleeperSofa не наследуется напрямую от класса Furniture . Сначала Furnitur e наследуют классы Bed и Sofa, а уж потом SleeperSofa наследуется от этих классов. Класс SleeperSofa выглядит в памяти так, как показано на рис. 28.3.

 

 


Furniture

 

Члены Bed

(без членов Furniture) Furniture

Члены Sofa

(без членов Furniture)

 

Уникальные члены класса SleeperSofa


 

_ Часть, наследованная от класса Bed

 

наследованная от класса Sofa


 

Полный

-объест SleeperSofa


 

Рис. 28.3. Расположение класса SleeperSofa в памяти

Как видите, SleeperSofa состоит из класса Bed, за которым в полном составе следует класс Sofa, а после него — уникальные члены класса SleeperSofa. Каждый из подобъектов класса SleeperSofa имеет свою собственную часть Furniture , по- скольку они оба наследуются от этого класса. В результате объекты класса Sleeper- Sofa содержат два объекта класса Furniture .

Таким образом, становится ясно, что я не сумел создать иерархию, показанную на рис. 28.2. Иерархия наследования, которая была создана в результате выполнения предыдущей программы, показана на рис. 28.4.

SleeperSofa содержит два объекта класса Furniture , что является явной бессмыслицей! Я хочу, чтобы SleeperSofa наследовал только одну копию Furnitur e и чтобы Bed и Sofa имели к ней доступ. В C++ это достигается виртуальным наследованием, поскольку в этом случае используется ключевое слово virtual .

 









ЧастьV.Полезныеособен


"urniture

weight

 

Bed


Furniture

weight

 

Sofa                 j

1


 

 

\

SleeperSofa

 

Рис. 28.4. Результат попытки создания иерархии классов

Я ненавижу перегрузку терминов (что произошло в данном случае) — ведь виртуальное наследование не имеет ничего общего с виртуальными

функциями!

Вооруженный новыми знаниями, я возвращаюсь к классу SleeperSofa и реали- зую его так, как показано ниже.

 

class f

 

Furnit ure int

 

class Bed : virtual public Furniture public:

Bed() {}

void

 

class Sofa : virtual public Furniture

 

Sofa() {}

void     { \

};

class SleeperSofa : public Bed, public Sofa

{


 

void


:        (}


Глава28.Множественноенаследование      311


void

 

cout « = "

<<

«

 

Обратите внимание на ключевое слово virtual , использующееся при наследова- нии классов Bed и Sofa от класса Furniture . Оно означает примерно

"Дайте-ка мне копию Furniture , но если она уже существует, то я использую имен- но ее". В итоге класс SleeperSofa будет выглядеть, как показанно на рис. 28.5.

 

 

Furniture

 


Члены Bed

(без членов Furniture)

Члены Sofa

(без членов Furniture)

 

Уникальныечлены класса SleeperSofa


 

Полный SleeperSofa


 

Рис. 28.5. Расположение класса SleeperSofa в памяти при использованиивиртуальногонаследования

Из этого рисунка видно, что класс SleeperSofa содержит Furniture , а также части классов Bed и Sofa, не содержащие Furniture . Далее находятся уникальные для класса SleeperSofa члены (элементы в памяти не обязательно будут располагать- ся именно в таком порядке, но в данном обсуждении это несущественно).

Теперь обращение к члену weight в функции fn (} не многозначно, поскольку SleeperSofa содержит только одну копию Furniture . Наследуя этот класс вирту- ально, мы получили желаемую структуру наследования (см. рис. 28.2).

Если виртуальное наследование так хорошо решает проблему неоднозначности, почему оно не является нормой? Во-первых, потому, что виртуально наследуемый класс обраба- тывается иначе, чем обычный наследуемый базовый класс, что, в частности, выражается в повышенных накладных расходах. Во-вторых, у вас может появиться желание иметь две копии базового класса (хотя это случается весьма редко). Вспомним наши старые упраж- нения со студентами и преподавателями и допустим,  что (помощник преподавателя) является одновременно и Teacher (преподавателем) и Student (студентом), которые, в свою очередь,        подклассами Academician. Если универ- ситет даст помощнику преподавателя два идентификатора — и студента и преподавателя, то классу TeacherAssistant понадобятся две копии класса Academician.

 

 

При конструировании объектов с использованием множественного наследования должен выполняться ряд правил.

 











ЧастьV.Полезныеособенно


1. Сначала вызываются конструкторы для каждого виртуального базового класса в порядке наследования.

2. Затем вызываются конструкторы каждого невиртуального базового класса в

3. После этого вызываются конструкторы всех объектов-членов класса в том порядке, в котором эти объекты-члены объявлены в классе.

4. И наконец, вызывается конструктор самого класса.

Обратите внимание, что базовые классы конструируются в порядке наследования, а не в порядке расположения в строке конструктора.

 

 

Должен признаться, что  не             кто работает с объектно-ориентированным про- считают механизм множественного наследования удачным. Кроме

многие объектно-ориентированные языки вообще не поддерживают множест- венного наследования, реализация которого, кстати, далеко не самая простая Конечно, множественное наследование — это проблема компилятора (вернее, того, кто пишет компилятор). Однако оно требует больших накладных расходов по сравне- нию с программой с обычным наследованием, а эти накладные расходы становятся уже проблемой программиста.

Не менее важно и то, что множественное наследование открывает путь к дополни- тельным ошибкам. Во-первых, неоднозначность, подобная описанной в предыдущем разделе, может превратиться в большую проблему. Во-вторых, при наличии множест- венного наследования преобразования указателя на подкласс в указатель на базовый класс иногда приводят к запутанным и непонятным изменениям этого указателя. Все эти тонкости я оставляю на совести разработчиков языка и компилятора, но хочу продемонстрировать программу, которая из-за этого может привести к непредсказуе- мым результатам:

 

 

class       mem;};

class  (int mem;);

class Subclass : public Basel, public Base2 {};

 

void         pSC)

{

Basel* =

Base2 pB2

(void*)pB2)

 

out <<'         численно     ;

 

 

int main(int args, char* pArgs Subclass sc;

return


Поделиться:



Последнее изменение этой страницы: 2019-04-19; Просмотров: 174; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.022 с.)
Главная | Случайная страница | Обратная связь