Этот сайт использует cookies. Продолжение работы с сайтом означает, что Вы согласны!
Множественное наследование
Язык 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();
Помощь сайту
ЮMoney (Yandex-деньги): 410011140483022
ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов