Virtual inheritance

From Seo Wiki - Search Engine Optimization and Programming Languages

Jump to: navigation, search
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 Animals 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

ja:仮想継承 ru:Виртуальное наследование

Personal tools

Served in 0.082 secs.