
Virtual inheritance is aC++ technique that ensures only one copy of abase class's member variables areinherited by grandchild derived classes. Without virtual inheritance, if two classesB andC inherit from a classA, and a classD inherits from bothB andC, thenD will contain two copies ofA's member variables: one viaB, and one viaC. These will be accessible independently, usingscope resolution.
Instead, if classesB andC inherit virtually from classA, then objects of classD will contain only one set of the member variables from classA.
This feature is most useful formultiple inheritance, as it makes the virtual base a commonsubobject for the deriving class and all classes that are derived from it. This can be used to avoid thediamond 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 ofD, not a class derived indirectly through a base (B orC).[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 thevirtualkeyword.
Consider the following class hierarchy.
classAnimal{public:virtual~Animal()=default;// Explicitly show that the default class destructor will be made.virtualvoideat(){}};classMammal:publicAnimal{public:virtualvoidbreathe(){}};classWingedAnimal:publicAnimal{public:virtualvoidflap(){}};// A bat is a winged mammalclassBat:publicMammal,publicWingedAnimal{};
As declared above, a call tobat.eat() is ambiguous because there are twoAnimal (indirect) base classes inBat, so anyBat object has two differentAnimal base class subobjects. So, an attempt to directly bind a reference to theAnimal subobject of aBat object would fail, since the binding is inherently ambiguous:
Batbat;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 convertbat to either base class subobject:
Batbat;Animal&mammal=static_cast<Mammal&>(bat);Animal&winged=static_cast<WingedAnimal&>(bat);
In order to calleat(), the same disambiguation, or explicit qualification is needed:static_cast<Mammal&>(bat).eat() orstatic_cast<WingedAnimal&>(bat).eat() or alternativelybat.Mammal::eat() andbat.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 ofAnimal is probably unwanted, as we want to model that the relation (Bat is anAnimal) exists only once; that aBat is aMammal and is aWingedAnimal, does not imply that it is anAnimal twice: anAnimal base class corresponds to a contract thatBat implements (the "is a" relationship above really means "implements the requirements of"), and aBat only implements theAnimal contract once. The real world meaning of "is a only once" is thatBat should have only one way of implementingEat, not two different ways, depending on whether theMammal view of theBat is eating, or theWingedAnimal view of theBat. (In the first code example we see thatEat is not overridden in eitherMammal orWingedAnimal, so the twoAnimal 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 asdiamond inheritance (seeDiamond problem) because the inheritance diagram is in the shape of a diamond. Virtual inheritance can help to solve this problem.
We can re-declare our classes as follows:
classAnimal{public:virtual~Animal()=default;virtualvoideat(){}};// Two classes virtually inheriting Animal:classMammal:virtualpublicAnimal{public:virtualvoidbreathe(){}};classWingedAnimal:virtualpublicAnimal{public:virtualvoidflap(){}};// A bat is still a winged mammalclassBat:publicMammal,publicWingedAnimal{};
TheAnimal portion ofBat::WingedAnimal is now thesameAnimal instance as the one used byBat::Mammal, which is to say that aBat has only one, shared,Animal instance in its representation and so a call toBat::Eat is unambiguous. Additionally, a direct cast fromBat toAnimal is also unambiguous, now that there exists only oneAnimal instance whichBat could be converted to.
The ability to share a single instance of theAnimal parent betweenMammal andWingedAnimal is enabled by recording the memory offset between theMammal orWingedAnimal members and those of the baseAnimal within the derived class. However this offset can in the general case only be known at runtime, thusBat must become (vpointer,Mammal,vpointer,WingedAnimal,Bat,Animal). There are twovtable pointers, one per inheritance hierarchy that virtually inheritsAnimal. In this example, one forMammal and one forWingedAnimal. The object size has therefore increased by two pointers, but now there is only oneAnimal and no ambiguity. All objects of typeBat will use the same vpointers, but eachBat object will contain its own uniqueAnimal object. If another class inherits fromMammal, such asSquirrel, then the vpointer in theMammal part ofSquirrel will generally be different to the vpointer in theMammal part ofBat though they may happen to be the same if theSquirrel class is the same size asBat.
This example to illustrates a case where base classA has a constructor variablemsg and an additional ancestorE is derived from grandchild classD.
A / \ B C \ / D | E
Here,A must be constructed in bothD andE. Further, inspection of the variablemsg illustrates how classA becomes a direct base class of its deriving class, as opposed to a base class of any intermediate deriving classed betweenA and the final deriving class.
importstd;usingstd::string;classA{private:stringmsg;public:explicitA(conststring&s):msg{s}{}voidtest(){std::println("Hello from A: {}",msg);}};// B, C inherit A virtuallyclassB:virtualpublicA{public:B():A("instance of B"){}};classC:virtualpublicA{public:C():A("instance of C"){}};// since B, C inherit A virtually, A must be constructed in each child// B() and C() constructors can be omittedclassD:publicB,publicC{public:D():A("instance of D"),B(),C(){}};// D() constructor can be omittedclassE:publicD{public:E():A("instance of E"),D(){}};// breaks without constructing A:// class D: public B, public C {// public:// D():// B(), C() {}// };// breaks without constructing A// class E: public D {// public:// E():// D() {}// };intmain(intargc,char*argv[]){Dd;d.test();// prints: "hello from A: instance of D"Ee;e.test();// prints: "hello from A: instance of E"}
Suppose a pure virtual method is defined in the base class. If a deriving class inherits the base class virtually, then the pure virtual method does not need to be defined in that deriving class. However, if the deriving class does not inherit the base class virtually, then all virtual methods must be defined.
importstd;usingstd::string;classA{protected:stringmsg;public:explicitA(conststring&s):msg{s}{}voidtest(){std::println("Hello from A: {}",msg);}virtualvoidpureVirtualTest()=0;};// since B, C inherit A virtually, the pure virtual method pureVirtualTest doesn't need to be definedclassB:virtualpublicA{public:explicitB([[maybe_unused]]conststring&s=""):A("instance of B"){}};classC:virtualpublicA{public:explicitC([[maybe_unused]]conststring&s=""):A("instance of C"){}};// since B, C inherit A virtually, A must be constructed in each child// however, since D does not inherit B, C virtually, the pure virtual method in A *must be defined*classD:publicB,publicC{public:explicitD([[maybe_unused]]conststring&s=""):A("instance of D from constructor A"),B("instance of D from constructor B"),C("instance of D from constructor C"){}voidpureVirtualTest()override{std::println("Pure virtual hello from: {}",msg);}};// it is not necessary to redefine the pure virtual method after the parent defines itclassE:publicD{public:explicitE([[maybe_unused]]conststring&s=""):A("instance of E from constructor A"),D("instance of E from constructor D"){}};intmain(intargc,char*argv[]){Dd("d");d.test();// Hello from A: instance of D from constructor Ad.pureVirtualTest();// Pure virtual hello from: instance of D from constructor AEe("e");e.test();// Hello from A: instance of E from constructor Ae.pureVirtualTest();// Pure virtual hello from: instance of E from constructor A}
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.