- •Концепция класса
- •Переменные класса
- •Методы класса
- •Наследование
- •Конструктор и деструктор
- •Виды памяти и создание объектов
- •Управление доступом к переменным и методам класса
- •Интерфейс
- •Исключения
- •Шаблоны
- •Введение в наследование
- •Множественное наследование классов
- •Преобразование типов
- •Виртуальные функции
-
Множественное наследование классов
Класс может служить базовым сразу для нескольких производных классов. Во многих случаях взаимодействие концепций, привлекаемых к решению задачи (формализацией чего является взаимодействие классов в программе), требует отношений более общего вида. Такие отношения могут быть получены в случае, когда производный класс строится на основе нескольких базовых; при этом говорят о множественном наследовании классов.
Внешне построение производного класса на основе нескольких базовых выглядит так: вместо имени одного базового класса (вместе с его атрибутом – public или private) используется список имен, разделенных запятыми. Например:
class A { /* … */ };
class B { /* … */ };
class C: public A, private B { /* … */};
Как обычно, атрибут private может быть опущен. Передача аргументов конструкторам базовых классов из конструктора производного класса производится так же, как и в случае без множественного наследования:
C::C (int a, char * str):A(a),B(str) { /* … */ }
Но реализация множественного наследования привела к появлению целого ряда проблем, главными из которых являются три:
а) как поступить, если в объект производного типа будут входить более одного объекта одного и того же базового типа;
б) как выбрать нужный член класса, если его имя присутствует более чем в одном базовом классе, и что при этом считать неопределенностью;
в) в каком порядке должно происходить создание и уничтожение подобъектов.
При построении производного класса с использованием множественного наследования упоминание в списке базовых классов одного и того же класса более одного раза запрещено:
class B : public A, public A { /* … */ }; // ошибка
Тем не менее, один и тот же базовый класс может использоваться при построении производного класса более одного раза следующим образом:
class Base { /* … */ };
class A : public Base { /* … */ };
class B : public Base { /* … */ };
class Derived : public A, public B { /* … */ };
В этом случае объект типа Derived будет содержать два различных подобъекта типа Base. Существуют ситуации в которых необходимо, чтобы подобъект типа Base в объекте типа Derived появился только один раз. Язык C++ позволяет программисту реализовать оба варианта.
Обеспечить создание только одного подобъекта базового типа можно, описав данный класс как виртуальный базовый класс:
class Base { /* … */ };
class A : public virtual Base { /* … */ };
class B : public virtual Base { /* … */ };
class Derived : public A, public B { /* … */ };
Теперь при создании объекта типа Derived будет создан только один подобъект типа Base, и его члены будут использоваться функциями-членами как класса A, так и B.
Правила создания подобъектов при использовании множественного наследования можно сформулировать следующим образом:
а) При отсутствии виртуальных базовых классов сначала происходит создание подобъектов базовых типов, причем эти подобъекта создаются в том же порядке, в котором соответствующие классы появились в списке базовых классов для данного производного класса; после этого происходит инициализация данных – членов производного класса в том порядке, в котором они появились в определении этого класса; и, наконец, выполняется конструктор производного класса. Деструкторы вызываются в обратном порядке.
б) При наличии виртуальных базовых классов они создаются до любого из своих производных классов; виртуальные базовые классы создаются ранее невиртуальных базовых классов; виртуальные базовые классы из списка создаются в порядке их появления в списке.
До сих пор все, что говорилось о производных классах, сводится к следующему: концепция производных классов – просто более удобный способ построения нового класса. Но преимущество концепции производных классов не исчерпывается удобством создания. Важным является то обстоятельство, что производный класс рассматривается не как совершенно самостоятельный класс, а как свой базовый класс с некоторыми дополнительными возможностями.