Virtual inheritance
This article does not cite any references or sources. Please help improve this article by adding citations to reliable sources. Unsourced material may be challenged and removed. (December 2009) |
- For inheritance of virtual functions, see virtual function.
In the C++ programming language, virtual inheritance is a kind of inheritance that can be used under multiple inheritance. When a class C has a direct or indirect base class X, it may inherit it indirectly through several inheritance lines. All the virtual occurences of X will result in a single internal instance of X within an instance of C. This can be used to solve some problems (particularly the "diamond problem"), by clarifying ambiguity over which ancestor class to use. It is used when inheritance is representing restrictions of a set rather than composition of parts. A multiply-inherited base class is denoted as virtual with the virtual
keyword.
The problem
Consider the following class hierarchy.
class Animal
{
public:
virtual void eat();
};
class Mammal : public Animal
{
public:
virtual void walk();
};
class WingedAnimal : public Animal
{
public:
virtual void flap();
};
// A bat is a winged mammal
class Bat : public Mammal, public WingedAnimal {};
Bat bat;
As declared above, a call to bat.eat()
is ambiguous. The ambiguity is caused by the way C++ implementations typically represent classes. In particular, inheritance is simply a matter of putting parent and child classes one after the other in memory. Thus, Bat
is represented as (Animal
,Mammal
,Animal
,WingedAnimal
,Bat
), which makes Animal
duplicated (in other words, Bat
owns two different Animal
instances, namely a Mammal::Animal
and a WingedAnimal::Animal
). To disambiguate, one would have to explicitly call either bat.Mammal::eat()
or bat.WingedAnimal::eat()
.
Similarly, an attempt to directly cast a Bat
object to an Animal
would fail, since the cast is ambiguous.
Animal a = (Animal)Bat(); //error: which Animal instance should a Bat cast into,
//a Mammal::Animal or a WingedAnimal::Animal?
As noted, the code would fail to compile, despite the fact that the two different Animal
s are 'identical' (for example, no virtual members of Animal
are being overridden by Mammal
or WingedAnimal
). To fix the code without using virtual inheritance, it would be necessary to first cast to one of the two inherited classes and then make the cast to Animal
.
Animal a = (Animal)(Mammal)Bat(); //ok: compiler chooses Mammal::Animal after
//cast to Mammal
This situation is sometimes referred to as diamond inheritance because the inheritance diagram is in the shape of a diamond. Virtual inheritance can help to solve this problem.
The solution
We can re-declare our classes as follows:
class Animal
{
public:
virtual void eat();
};
// Two classes virtually inheriting Animal:
class Mammal : public virtual Animal
{
public:
virtual void walk();
};
class WingedAnimal : public virtual Animal
{
public:
virtual void flap();
};
// A bat is still a winged mammal
class Bat : public Mammal, public WingedAnimal {};
The Animal
portion of Bat::WingedAnimal
is now the same Animal
instance as the one used by Bat::Mammal
, which is to say that a Bat
has only one, shared, Animal
instance in its representation and so a call to Bat::eat()
is unambiguous. Additionally, a direct cast from Bat
to Animal
is also unambiguous, now that there exists only one Animal
instance which Bat
could be converted to.
This is implemented by providing Mammal
and WingedAnimal
with a vtable pointer (or "vpointer") since, e.g., the memory offset between the beginning of a Mammal
and of its Animal
part is unknown until runtime. Thus Bat becomes (vpointer
,Mammal
,vpointer
,WingedAnimal
,Bat
,Animal
). There are two vtable pointers, one per inheritance hierarchy that virtually inherits Animal
. In this example, one for Mammal
and one for WingedAnimal
. The object size has therefore increased by two pointers, but now there is only one Animal
and no ambiguity. All objects of type Bat
will have the same vpointers, but each Bat
object will contain its own unique Animal
object. If another class inherits from Mammal
, such as Squirrel
, then the vpointer in the Mammal
object in a Squirrel
will be different from the vpointer in the Mammal
object in a Bat
, although they can still be essentially the same in the special case that the Squirrel
part of the object has the same size as the Bat
part, because then the distance from the Mammal
to the Animal
part is the same. The vtables are not really the same, but all essential information in them (the distance) is.
See also
If you like SEOmastering Site, you can support it by - BTC: bc1qppjcl3c2cyjazy6lepmrv3fh6ke9mxs7zpfky0 , TRC20 and more...