Virtual inheritance is a C++ technique that ensures only one copy of a base class's member variables are inherited by grandchild derived classes. Without virtual inheritance, if two classes B
and C
inherit from a class A
, and a class D
inherits from both B
and C
, then D
will contain two copies of A
's member variables: one via B
, and one via C
. These will be accessible independently, using scope resolution.
Instead, if classes B
and C
inherit virtually from class A
, then objects of class D
will contain only one set of the member variables from class A
.
This feature is most useful for multiple inheritance, as it makes the virtual base a common subobject for the deriving class and all classes that are derived from it. This can be used to avoid the diamond problem by clarifying ambiguity over which ancestor class to use, as from the perspective of the deriving class (D
in the example above) the virtual base (A
) acts as though it were the direct base class of D
, not a class derived indirectly through a base (B
or C
).[1][2]
It is used when inheritance represents restriction of a set rather than composition of parts. In C++, a base class intended to be common throughout the hierarchy is denoted as virtual with the virtual
keyword.
Consider the following class hierarchy.
struct Animal {
virtual ~Animal() = default; // Explicitly show that the default class destructor will be made.
virtual void Eat() {}
};
struct Mammal: Animal {
virtual void Breathe() {}
};
struct WingedAnimal: Animal {
virtual void Flap() {}
};
// A bat is a winged mammal
struct Bat: Mammal, WingedAnimal {};
As declared above, a call to bat.Eat
is ambiguous because there are two Animal
(indirect) base classes in Bat
, so any Bat
object has two different Animal
base class subobjects. So, an attempt to directly bind a reference to the Animal
subobject of a Bat
object would fail, since the binding is inherently ambiguous:
Bat bat;
Animal& animal = bat; // error: which Animal subobject should a Bat cast into,
// a Mammal::Animal or a WingedAnimal::Animal?
To disambiguate, one would have to explicitly convert bat
to either base class subobject:
Bat bat;
Animal& mammal = static_cast<Mammal&>(bat);
Animal& winged = static_cast<WingedAnimal&>(bat);
In order to call Eat
, the same disambiguation, or explicit qualification is needed: static_cast<Mammal&>(bat).Eat()
or static_cast<WingedAnimal&>(bat).Eat()
or alternatively bat.Mammal::Eat()
and bat.WingedAnimal::Eat()
. Explicit qualification not only uses an easier, uniform syntax for both pointers and objects but also allows for static dispatch, so it would arguably be the preferable method.
In this case, the double inheritance of Animal
is probably unwanted, as we want to model that the relation (Bat
is an Animal
) exists only once; that a Bat
is a Mammal
and is a WingedAnimal
, does not imply that it is an Animal
twice: an Animal
base class corresponds to a contract that Bat
implements (the "is a" relationship above really means "implements the requirements of"), and a Bat
only implements the Animal
contract once. The real world meaning of "is a only once" is that Bat
should have only one way of implementing Eat
, not two different ways, depending on whether the Mammal
view of the Bat
is eating, or the WingedAnimal
view of the Bat
. (In the first code example we see that Eat
is not overridden in either Mammal
or WingedAnimal
, so the two Animal
subobjects will actually behave the same, but this is just a degenerate case, and that does not make a difference from the C++ point of view.)
This situation is sometimes referred to as diamond inheritance (see Diamond problem) because the inheritance diagram is in the shape of a diamond. Virtual inheritance can help to solve this problem.
One of the problems that arises due to multiple inheritance is the diamond problem. A classical illustration of this is given by Bjarne Stroustrup (the creator of C++) in the following example:
This is something you find may be required if you are using multiple inheritance. In that case it is possible for a class to be derived from other classes which have the same base class. In such cases, without virtual inheritance, your objects will contain more than one subobject of the base type the base classes share. Whether this is what is the required effect depends on the circumstances. If it is not then you can use virtual inheritance by specifying virtual base classes for those base types for which a whole object should only contain one such base class subobject.