Virtual inheritance

Last updated
Diagram of diamond inheritance, a problem that virtual inheritance is trying to solve. Diamond inheritance.svg
Diagram of diamond inheritance, a problem that virtual inheritance is trying to solve.

Virtual inheritance is a C++ technique that ensures only one copy of a base class 's member variables are inherited by grandchild derived classes. Without virtual inheritance, if two classes B and C inherit from a class A, and a class D inherits from both B and C, then D will contain two copies of A's member variables: one via B, and one via C. These will be accessible independently, using scope resolution.

Contents

Instead, if classes B and C inherit virtually from class A, then objects of class D will contain only one set of the member variables from class A.

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 diamond 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 of D, not a class derived indirectly through a base (B or C). [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.

UML virtual inheritance.svg

structAnimal{virtual~Animal()=default;// Explicitly show that the default class destructor will be made.virtualvoidEat(){}};structMammal:Animal{virtualvoidBreathe(){}};structWingedAnimal:Animal{virtualvoidFlap(){}};// A bat is a winged mammalstructBat:Mammal,WingedAnimal{};

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:

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 convert bat to either base class subobject:

Batbat;Animal&mammal=static_cast<Mammal&>(bat);Animal&winged=static_cast<WingedAnimal&>(bat);

In order to call Eat, the same disambiguation, or explicit qualification is needed: static_cast<Mammal&>(bat).Eat() or static_cast<WingedAnimal&>(bat).Eat() or alternatively bat.Mammal::Eat() and bat.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 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:

structAnimal{virtual~Animal()=default;virtualvoidEat(){}};// Two classes virtually inheriting Animal:structMammal:virtualAnimal{virtualvoidBreathe(){}};structWingedAnimal:virtualAnimal{virtualvoidFlap(){}};// A bat is still a winged mammalstructBat:Mammal,WingedAnimal{};

The Animal portion of Bat::WingedAnimal is now the sameAnimal 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.

The ability to share a single instance of the Animal parent between Mammal and WingedAnimal is enabled by recording the memory offset between the Mammal or WingedAnimal members and those of the base Animal within the derived class. However this offset can in the general case only be known at runtime, thus Bat must become (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 use 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 part of Squirrel will generally be different to the vpointer in the Mammal part of Bat though they may happen to be the same if the Squirrel class is the same size as Bat.

Additional Example of Several Ancestors

This example to illustrates a case where base class A has a constructor variable msg and an additional ancestor E is derived from grandchild class D.

  A    / \   B   C    \ /     D    |   E 

Here, A must be constructed in both D and E. Further, inspection of the variable msg illustrates how class A becomes a direct base class of its deriving class, as opposed to a base class of any intermediate deriving classed between A and the final deriving class. The code below may be explored interactively here.

#include<string>#include<iostream>classA{private:std::string_msg;public:A(std::stringx):_msg(x){}voidtest(){std::cout<<"hello from A: "<<_msg<<"\n";}};// B,C inherit A virtuallyclassB:virtualpublicA{public:B():A("b"){}};classC:virtualpublicA{public:C():A("c"){}};// since B,C inherit A virtually, A must be constructed in each child// B() and C() constructors can be omittedclassD:publicB,C{public:D():A("d_a"),B(),C(){}};// D() constructor omittedclassE:publicD{public:E():A("e_a"){}};// breaks without constructing A// class D: public      B,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: d_a" Ee;e.test();// prints: "hello from A: e_a"}

Pure Virtual Methods

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. The code below may be explored interactively here.

#include<string>#include<iostream>classA{protected:std::string_msg;public:A(std::stringx):_msg(x){}voidtest(){std::cout<<"hello from A: "<<_msg<<"\n";}virtualvoidpure_virtual_test()=0;};// since B,C inherit A virtually, the pure virtual method pure_virtual_test doesn't need to be definedclassB:virtualpublicA{public:B(std::stringx):A("b"){}};classC:virtualpublicA{public:C(std::stringx):A("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,C{public:D(std::stringx):A("d_a"),B("d_b"),C("d_c"){}voidpure_virtual_test()override{std::cout<<"pure virtual hello from: "<<_msg<<"\n";}};// it is not necessary to redefine the pure virtual method after the parent defines itclassE:publicD{public:E(std::stringx):A("e_a"),D("e_d"){}};intmain(intargc,char**argv){Dd("d");d.test();// hello from A: d_ad.pure_virtual_test();// pure virtual hello from: d_aEe("e");e.test();// hello from A: e_ae.pure_virtual_test();// pure virtual hello from: e_a}

Related Research Articles

Multiple inheritance is a feature of some object-oriented computer programming languages in which an object or class can inherit features from more than one parent object or parent class. It is distinct from single inheritance, where an object or class may only inherit from one particular object or class.

<span class="mw-page-title-main">C++</span> General-purpose programming language

C++ is a high-level, general-purpose programming language created by Danish computer scientist Bjarne Stroustrup. First released in 1985 as an extension of the C programming language, it has since expanded significantly over time; as of 1997, C++ has object-oriented, generic, and functional features, in addition to facilities for low-level memory manipulation for systems like microcomputers or to make operating systems like Linux or Windows. It is usually implemented as a compiled language, and many vendors provide C++ compilers, including the Free Software Foundation, LLVM, Microsoft, Intel, Embarcadero, Oracle, and IBM.

In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. It is a kind of lazy evaluation that refers specifically to the instantiation of objects or other resources.

In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other instances of the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern as well as to the Open-Closed Principle, by allowing the functionality of a class to be extended without being modified. Decorator use can be more efficient than subclassing, because an object's behavior can be augmented without defining an entirely new object.

Multiple dispatch or multimethods is a feature of some programming languages in which a function or method can be dynamically dispatched based on the run-time (dynamic) type or, in the more general case, some other attribute of more than one of its arguments. This is a generalization of single-dispatch polymorphism where a function or method call is dynamically dispatched based on the derived type of the object on which the method has been called. Multiple dispatch routes the dynamic dispatch to the implementing function or method using the combined characteristics of one or more arguments.

Template metaprogramming (TMP) is a metaprogramming technique in which templates are used by a compiler to generate temporary source code, which is merged by the compiler with the rest of the source code and then compiled. The output of these templates can include compile-time constants, data structures, and complete functions. The use of templates can be thought of as compile-time polymorphism. The technique is used by a number of languages, the best-known being C++, but also Curl, D, Nim, and XL.

<span class="mw-page-title-main">D (programming language)</span> Multi-paradigm system programming language

D, also known as dlang, is a multi-paradigm system programming language created by Walter Bright at Digital Mars and released in 2001. Andrei Alexandrescu joined the design and development effort in 2007. Though it originated as a re-engineering of C++, D is now a very different language. As it has developed, it has drawn inspiration from other high-level programming languages. Notably, it has been influenced by Java, Python, Ruby, C#, and Eiffel.

In object-oriented programming such as is often used in C++ and Object Pascal, a virtual function or virtual method is an inheritable and overridable function or method that is dispatched dynamically. Virtual functions are an important part of (runtime) polymorphism in object-oriented programming (OOP). They allow for the execution of target functions that were not precisely identified at compile time.

In computer programming, run-time type information or run-time type identification (RTTI) is a feature of some programming languages that exposes information about an object's data type at runtime. Run-time type information may be available for all types or only to types that explicitly have it. Run-time type information is a specialization of a more general concept called type introspection.

<span class="mw-page-title-main">Method overriding</span> Language feature in object-oriented programming

Method overriding, in object-oriented programming, is a language feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes. In addition to providing data-driven algorithm-determined parameters across virtual network interfaces, it also allows for a specific type of polymorphism (subtyping). The implementation in the subclass overrides (replaces) the implementation in the superclass by providing a method that has same name, same parameters or signature, and same return type as the method in the parent class. The version of a method that is executed will be determined by the object that is used to invoke it. If an object of a parent class is used to invoke the method, then the version in the parent class will be executed, but if an object of the subclass is used to invoke the method, then the version in the child class will be executed. This helps in preventing problems associated with differential relay analytics which would otherwise rely on a framework in which method overriding might be obviated. Some languages allow a programmer to prevent a method from being overridden.

<i>Modern C++ Design</i> Book by Andrei Alexandrescu

Modern C++ Design: Generic Programming and Design Patterns Applied is a book written by Andrei Alexandrescu, published in 2001 by Addison-Wesley. It has been regarded as "one of the most important C++ books" by Scott Meyers.

A class in C++ is a user-defined type or data structure declared with any of the keywords class, struct or union that has data and functions as its members whose access is governed by the three access specifiers private, protected or public. By default access to members of a C++ class declared with the keyword class is private. The private members are not accessible outside the class; they can be accessed only through member functions of the class. The public members form an interface to the class and are accessible outside the class.

The curiously recurring template pattern (CRTP) is an idiom, originally in C++, in which a class X derives from a class template instantiation using X itself as a template argument. More generally it is known as F-bound polymorphism, and it is a form of F-bounded quantification.

C++11 is a version of a joint technical standard, ISO/IEC 14882, by the International Organization for Standardization (ISO) and International Electrotechnical Commission (IEC), for the C++ programming language. C++11 replaced the prior version of the C++ standard, named C++03, and was later replaced by C++14. The name follows the tradition of naming language versions by the publication year of the specification, though it was formerly named C++0x because it was expected to be published before 2010.

In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral (null) behavior. The null object design pattern, which describes the uses of such objects and their behavior, was first published as "Void Value" and later in the Pattern Languages of Program Design book series as "Null Object".

In object-oriented programming, a virtual base class is a nested inner class whose functions and member variables can be overridden and redefined by subclasses of an outer class. Virtual classes are analogous to virtual functions.

This article describes the syntax of the C# programming language. The features described are compatible with .NET Framework and Mono.

In the C++ programming language, dominance refers to a particular aspect of C++ name lookup in the presence of Inheritance. When the compiler computes the set of declarations to which a particular name might refer, declarations in very-ancestral classes which are "dominated" by declarations in less-ancestral classes are hidden for the purposes of name lookup. In other languages or contexts, the same principle may be referred to as "name masking" or "shadowing".

In computer programming, variadic templates are templates that take a variable number of arguments.

<span class="mw-page-title-main">Composition over inheritance</span> Software design pattern

Composition over inheritance in object-oriented programming (OOP) is the principle that classes should favor polymorphic behavior and code reuse by their composition over inheritance from a base or parent class. Ideally all reuse can be achieved by assembling existing components, but in practice inheritance is often needed to make new ones. Therefore inheritance and object composition typically work hand-in-hand, as discussed in the book Design Patterns (1994).

References

  1. Milea, Andrei. "Solving the Diamond Problem with Virtual Inheritance". 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:
  2. McArdell, Ralph (2004-02-14). "C++/What is virtual inheritance?". All Experts. Archived from the original on 2010-01-10. 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.