Jump to content

Virtual inheritance

From Wikipedia, the free encyclopedia

This is an old revision of this page, as edited by RzR~enwiki (talk | contribs) at 12:34, 5 May 2014. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

For inheritance of virtual functions, see virtual function.

Virtual inheritance is a technique used in object-oriented programming, where a particular base class in an inheritance hierarchy is declared to share its member data instances with any other inclusions of that same base in further derived classes. For example, if class A is normally (non-virtually) derived from class X (assumed to contain data members), and class B likewise, and class C inherits from both classes A and B, it will contain two sets of the data members associated with class X (accessible independently, often with suitable disambiguating qualifiers). But if class A is virtually derived from class X instead, then objects of class C will contain only one set of the data members from class X. The best-known language that implements this feature is C++.

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 problem of ambiguous hierarchy composition (known as the "diamond problem") by clarifying ambiguity over which ancestor class to use, as from the perspective of the deriving class (C in the example above) the virtual base (X) acts as though it were the direct base class of C, not a class derived indirectly through its base (A).[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.

class Animal {
 public:
  virtual void eat();
};

class Mammal : public Animal {
 public:
  virtual void breathe();
};

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 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 b;
Animal &a = b; // 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 b;
Animal &mammal = static_cast<Mammal&> (b); 
Animal &winged = static_cast<WingedAnimal&> (b);

In order to call eat(), the same disambiguation is needed: static_cast<Mammal&>(bat).eat() or static_cast<WingedAnimal&>(bat).eat().

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.

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 breathe();
};

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 an information of 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

References

  1. ^ Andrei Milea. "Solving the Diamond Problem with Virtual Inheritance". http://www.cprogramming.com/: Cprogramming.com. Retrieved 2010-03-08. 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: {{cite web}}: External link in |location= (help)
  2. ^ Ralph McArdell (2004-02-14). "C++/What is virtual inheritance?". http://en.allexperts.com/: All Experts. Retrieved 2010-03-08. 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. {{cite web}}: External link in |location= (help)