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

Язык C++ поддерживает также множественное наследование. В этом случае используется следующий формат объявления производного класса:

class <Производный класс>: [<Модификатор доступа>] <Базовый класс1>,
                           [<Модификатор доступа>] <Базовый класс2>,
                           [...,
                           <Модификатор доступа>] <Базовый классN>
{
   <Объявления членов класса>
} [<Объявления переменных через запятую>];

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

<Название производного класса>(<Параметры>) : 
             <Название базового класса1>(<Значения>),
             <Название базового класса2>(<Значения>),
              ...,
             <Название базового классаN>(<Значения>)
{
   // Тело конструктора
}

Значения конструкторам базовых классов передаются через список инициализации, внутри которого указываются названия базовых классов через запятую после которых внутри круглых скобок передаются значения. В качестве примера продемонстрируем порядок вызова конструкторов и деструкторов при множественном наследовании, а также передачу значений конструкторам базовых классов (листинг 13.26).

Листинг 13.26. Множественное наследование

#include <iostream>

class A {
public:
   int x;
   A(int a) : x(a) {          // Конструктор
      std::cout << "A::A()" << std::endl;
   }
   ~A() { std::cout << "A::~A()" << std::endl; }
   void func1() { std::cout << "A::func1()" << std::endl; }
};
class B {
public:
   int y;
   B(int a) : y(a) {          // Конструктор
      std::cout << "B::B()" << std::endl;
   }
   ~B() { std::cout << "B::~B()" << std::endl; }
   void func2() { std::cout << "B::func2()" << std::endl; }
};
class C: public A, public B { // Класс C наследует классы A и B
public:
   int z;
   C(int a, int b, int c)     // Конструктор
      : A(b), B(c), z(a) {    // Передаем значения
      std::cout << "C::C()" << std::endl;
   }
   ~C() { std::cout << "C::~C()" << std::endl; }
};

int main() {
   C obj(10, 20, 30);
   obj.func1();
   obj.func2();
   std::cout << "A::x = " << obj.x << " B::y = " << obj.y
             << " C::z = " << obj.z << std::endl;
   return 0;
}

Результат выполнения программы:

A::A()
B::B()
C::C()
A::func1()
B::func2()
A::x = 20 B::y = 30 C::z = 10
C::~C()
B::~B()
A::~A()

Если производный класс наследует несколько классов, которые в свою очередь наследуют один и тот же базовый класс, то возникает неоднозначность, т. к. один и тот же член базового класса будет присутствовать в нескольких классах. Рассмотрим эту ситуацию на примере (листинг 13.27).

Листинг 13.27. Неоднозначность при множественном наследовании

#include <iostream>

class A {
public:
   void func1() { std::cout << "A::func1()" << std::endl; }
};
class B : public A {
public:
   void func2() { std::cout << "B::func2()" << std::endl; }
};
class C : public A {
public:
   void func3() { std::cout << "C::func3()" << std::endl; }
};
class D : public B, public C { // Класс D наследует классы B и C
};

int main() {
   D obj;
   // obj.func1();   // Неоднозначность. Из B или из C?
   obj.B::func1();   // Берем из класса B
   obj.C::func1();   // Берем из класса C
   obj.func2();      // Однозначно
   obj.func3();      // Однозначно
   return 0;
}

В этом примере классы B и C являются наследниками класса A, в котором объявлен метод func1(). Класс D наследует классы B и C. Таким образом метод func1() существует в двух экземплярах. Если создать экземпляр класса D и попытаться обратиться к методу, то компилятор выведет сообщение об ошибке. Одним из способов разрешения неоднозначности является явное указание класса и оператора :: перед названием метода при вызове:

obj.B::func1();   // Берем из класса B
obj.C::func1();   // Берем из класса C

Нужны ли два одинаковых метода? Скорее всего нет. Чтобы метод был только в единственном экземпляре следует объявить классы B и C виртуальными. Для этого в списке наследования перед модификатором доступа необходимо указать ключевое слово virtual. Пример:

class B : virtual public A {
public:
   void func2() { std::cout << "B::func2()" << std::endl; }
};
class C : virtual public A {
public:
   void func3() { std::cout << "C::func3()" << std::endl; }
};

В этом случае никакой неоднозначности не будет, поэтому можно вызвать метод без явного указания базового класса:

D obj;
obj.func1();

Учебник C++ (MinGW-W64)
Учебник C++ (MinGW-W64) в формате PDF

Помощь сайту

ЮMoney (Yandex-деньги): 410011140483022

ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов