Virtual inheritance

From Wikipedia, the free encyclopedia

For inheritance of virtual functions, see virtual function.

In C++, virtual inheritance is a kind of inheritance that solves some of the problems caused by multiple inheritance (particularly the "diamond problem") by clarifying ambiguity over which ancestor class members 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.

[edit] The problem

Consider the following class hierarchy.

class Animal {
    virtual void Eat();
};

class Mammal : public Animal {
 public:
  virtual Color GetHairColor();
};
class WingedAnimal : public Animal {
 public:
  virtual void Flap();
};

// A bat is a winged mammal
class Bat : public Mammal, public WingedAnimal {};

But how does a Bat Eat()? As declared above, a call to Bat.Eat() is ambiguous. One would have to call either Bat.WingedAnimal::Eat() or Bat.Mammal::Eat(). The problem is that semantics of conventional multiple inheritance do not model reality. In a sense, an Animal is only an Animal once; a Bat is a Mammal and a WingedAnimal, but the Animalness of a Bat's Mammalness is the same Animalness as that of its WingedAnimalness.

This is the problem that virtual inheritance works to solve.

[edit] Class representation

Before going further it is helpful to consider how classes are represented in C++. In particular, inheritance is simply a matter of putting parent and child class one after the other in memory.

[edit] Solution

We can redeclare our classes as follows:

// Two classes virtually inheriting Animal:
class Mammal : public virtual Animal {
 public:
  virtual Color GetHairColor();
};
class WingedAnimal : public virtual Animal {
 public:
  virtual void Flap();
};
// A bat is still a winged mammal
class Bat : public Mammal, public WingedAnimal {};

Now the Animal portion of Bat::WingedAnimal is the same Animal as the one used by Bat::Mammal, which is to say that a Bat has only one Animal in its representation and so a call to Bat::Eat() is unambiguous.

This is implemented by providing Mammal and WingedAnimal with a vtable since, e.g., the memory offset between the beginning of a Mammal and of its Animal part is unknown until runtime.