Виртуальное наследование
Виртуа́льное насле́дование (англ. virtual inheritance) в языке программирования C++ — один из вариантов наследования, который нужен для решения некоторых проблем, порождаемых наличием возможности множественного наследования (особенно «ромбовидного наследования»), путём разрешения неоднозначности того, методы которого из суперклассов (непосредственных классов-предков) необходимо использовать. Оно применяется в тех случаях, когда множественное наследование вместо предполагаемой полной композиции свойств классов-предков приводит к ограничению доступных наследуемых свойств вследствие неоднозначности. Базовый класс, наследуемый множественно, определяется виртуальным с помощью ключевого слова Суть проблемыРассмотрим следующую иерархию классов: class Animal
{
public:
virtual void eat(); // Метод определяется для данного класса
...
};
class Mammal : public Animal
{
public:
Color getHairColor();
...
};
class WingedAnimal : public Animal
{
public:
void flap();
...
};
// A bat is a winged mammal
class Bat : public Mammal, public WingedAnimal {}; //<--- обратите внимание, что метод eat() не переопределен в Bat
Bat bat;
Для вышеприведенного кода вызов Такая ситуация обычно именуется «ромбовидным наследованием» и представляет собой проблему, которую призвано решить виртуальное наследование. Представление классаПрежде чем продолжить, полезным будет рассмотреть, как классы представляются в C++. В частности, при наследовании классы предка и наследника просто помещаются в памяти друг за другом. Таким образом объект класса Bat это на самом деле последовательность объектов классов (Animal,Mammal,Animal,WingedAnimal,Bat), размещенных последовательно в памяти, при этом Animal повторяется дважды, что и приводит к неоднозначности. РешениеМы можем переопределить наши классы следующим образом: class Animal
{
public:
virtual void eat();
...
};
// Two classes virtually inheriting Animal:
class Mammal : public virtual Animal // <--- обратите внимание на ключевое слово virtual
{
public:
Color getHairColor();
...
};
class WingedAnimal : public virtual Animal // <--- обратите внимание на ключевое слово virtual
{
public:
void flap();
...
};
// A bat is still a winged mammal
class Bat : public Mammal, public WingedAnimal {};
Теперь, часть Виртуальное наследование реализуется через добавление указателей в классы ПримерЧтобы понять суть виртуального наследования без лишнего "шума", следует рассмотреть следующий пример: #include <iostream>
class A {
public:
virtual int foo() {
return 1;
}
};
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};
int main () {
D d;
std::cout << d.foo();
return 0;
}
Если убрать ключевое слово virtual, то метод foo() не может быть определён однозначно и в результате не будет доступен, как и объект класса D — код не скомпилируется. См. такжеЛитература
|