Программирование >>  Аргументация конструирования 

1 ... 97 98 99 [ 100 ] 101 102 103 ... 108



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

ff% ненавижу перегрузку терминов (что произошло в данном случае) - Y ВДЬ виртуальное наследование не имеет ничего общего с виртуальными ЧЖ функциями!

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

#include <iostreair,.h>

class Furr.iture

public:

Furnit ure() () int weight;

class Bed : virtual public Furniture

public:

Bed() {}

void £leep(){)

class Sofa : virtual public Furniture С

public:

SofaO {} voidwatchTVO { \

class SleeperSofa : public Bed, public Sofa {

public;

SleeperSofa0 : Sofa (), Bed() (} void foldOut 0 ( }



void in()

SleeperSofa ss;

cout Bee = ss-weight \ n ;

Обратите внимание на ключевое слово 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).

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

fioHcfUfuftoioHue оъекй1оё

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



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

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

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

порядке, в котором эти объекты-члены объявлены в классе.

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

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

О/ЩищаМельнме ано/гонм ишх>0*сеап£енло1О наследованил

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

кто пишет компилятор). Однако оно требует больших накладных расходов по сравнению с программой с обычным наследованием, а эти накладные расходы становятся уже проблемой программиста.

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

clas ( mt mem;} ; class Ba;ic2 (Int mem; ) ;

class Subclass : public Basel, public Base2 {}; voi Class* pSC)

Basel* pBl = (Basel)pSC; Base2* pB2 - {Base2-)pSC; if ( {vr3id*)pBl == (void*)pB2) {

с out атели численна .ы\п ;

int main(int args, char* pArgs[П {

Subclass sc; fn(Ssc);

return



1 ... 97 98 99 [ 100 ] 101 102 103 ... 108

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика