Виртуальное наследование
Виртуальное наследование - это механизм C++, который
применяется при множественном наследовании для того,
чтобы избежать дублирования базового класса в иерархии.
Если два класса наследуются от одного общего базового
класса, а затем от них наследуется ещё один класс,
то без виртуального наследования этот общий базовый
класс будет присутствовать в конечном классе дважды.
Ключевое слово virtual указывается перед
именем базового класса в списке наследования и
говорит компилятору, что данный базовый класс должен
быть представлен в производных классах только в одном
экземпляре.
Синтаксис
class Base { };
class Derived1 : virtual public Base { };
class Derived2 : virtual public Base { };
class Final : public Derived1, public Derived2 { };
Ключевое слово virtual может стоять как перед
спецификатором доступа, так и после него. Оба варианта
равнозначны.
Пример
Рассмотрим классическую проблему ромбовидного
наследования без использования virtual:
#include <iostream>
using namespace std;
class Base
{
public:
int value = 10;
};
class Derived1 : public Base { };
class Derived2 : public Base { };
class Final : public Derived1, public Derived2
{
public:
void print()
{
// cout << value; // ошибка: неоднозначность
}
};
int main()
{
Final obj;
cout << obj.Derived1::value << endl;
cout << obj.Derived2::value << endl;
return 0;
}
Результат выполнения кода:
10
10
Здесь value существует в объекте дважды, и
обращение obj.value вызовет ошибку
неоднозначности.
Пример
Теперь применим виртуальное наследование и посмотрим, как изменится поведение:
#include <iostream>
using namespace std;
class Base
{
public:
int value = 10;
};
class Derived1 : virtual public Base { };
class Derived2 : virtual public Base { };
class Final : public Derived1, public Derived2
{
public:
void print()
{
cout << value << endl;
}
};
int main()
{
Final obj;
obj.print();
obj.value = 20;
obj.print();
return 0;
}
Результат выполнения кода:
10
20
Теперь value существует в единственном экземпляре,
и обращение к нему однозначно.
Пример
Виртуальное наследование также влияет на порядок вызова конструкторов. Рассмотрим пример:
#include <iostream>
using namespace std;
class Base
{
public:
Base() { cout << "Base" << endl; }
};
class Derived1 : virtual public Base
{
public:
Derived1() { cout << "Derived1" << endl; }
};
class Derived2 : virtual public Base
{
public:
Derived2() { cout << "Derived2" << endl; }
};
class Final : public Derived1, public Derived2
{
public:
Final() { cout << "Final" << endl; }
};
int main()
{
Final obj;
return 0;
}
Результат выполнения кода:
"Base"
"Derived1"
"Derived2"
"Final"
Конструктор виртуального базового класса вызывается один раз и до конструкторов промежуточных классов.
Смотрите также
-
ключевое слово
virtual,
которое объявляет виртуальные функции -
раздел
множественное наследование,
который описывает наследование от нескольких классов -
раздел
наследование,
который описывает базовый механизм наследования -
раздел
абстрактные классы,
которые часто используются с виртуальным наследованием