Multiple Inheritance in C++
Sometimes it is necessary for a class to be derived from two or more base classes:
And so on. When this is the case, we are dealing with multiple inheritance, in which an object of the derived class has the attributes and behaviors of each of its base classes and can be considered to be an object of any one of its base classes.
Even single inheritance can be tricky, and is apparently overused and misused to a great extent in the world of software development. So, any cautions that may be offered with regard to inheritance in general certainly apply in spades to multiple inheritance, which comes with its own peculiar set of additional problems and gotchas. See the following sections for one particular kind of problem and its solution.
The following UML diagram (UML = Unified Modeling Language) illustrates the difference, in general, between single and multiple inheritance:
And the next diagram illustrates a multiple inheritance situation in which an amphibious vehicle is viewed as both a car and a boat (an example taken from Object-Oriented Programming in C++ by Nicolai Josuttis (John Wiley & Sons, 2001)):
The syntax for multiple inheritance (in this case) would be this:
class Amphibian : public Car, public Boat { //declarations }
Note that (as with single inheritance) multiple inheritance is private by default, so the keyword public is required to ensure that the inheritance is in fact public, which is what is usually desired.
The order of the base classes in the above syntax is relevant: it determines the order in which the base classes are constructed (before the derived part is constructed). Note, in this regard, that the order of the base class constructors in the initialization list of a derived class constructor is therefore irrelevant.
In the simple case of multiple inheritance shown above, there is usually no problem. But what if the two base classes themselves inherit from a single base class? In this case, the diagram, which is shown below, is sometimes called the "Deadly Diamond of Death" (or, by the more charitable, the "Deadly Diamond of Derivation") for reasons that we will now discuss. Begin by looking at the following diagram:
The problem with the situation shown in the above diagram is that since every data member in the Vehicle class is inherited by both the Car and the Boat classes, and the Amphibian class contains everything in a Car as well as everything in a Boat, there will be two of every data member in Vehicle in Amphibian, leading to ambiguity and confusion. To compound the difficulty, it may sometimes make sense to have these two copies and sometimes not.
For example, even an abstract Vehicle may be regarded as having a maximum speed, and an amphibious vehicle may have a different maximum speed as a car (land vehicle) than it has as a boat (water vehicle). So in this case it actually makes sense to have two "maximum speed" data members in the Amphibian class.
On the other hand, a Vehicle might also have a data member recording the year of manufacture, and we would want only one of these in our Amphibian class.
This is still a pretty simple example, but it is already getting messy, since we have at least three cases:
In this section we discuss further each of the situations mentioned at the end of the last section:
class Base1 : public Top { //declarations }and
class Base2 : public Top { //declarations }and then we follow this up with a multiple inheritance situation like this:
class Derived : public Base1, public Base2 { //declarations }In this case the class Derived will have two copies of each data member from Top.
This is another use for the C++ keyword virtual. The syntax is
class Base1 : public virtual Top { //declarations }
and similarly for the inheritance of Base2 from Top, though in either case the order of the keywords public and virtual may be interchanged.
To illustrate this we go back to Josuttis' Car-Boat-Amphibian example. In the diagram below, the inheritance from VehicleVirtual to VehicleNonVirtual is virtual, which means that in classes subsequently derived via multiple inheritance (such as Amphibian) the member yearOfManufacture will be unique. On the other hand, the derivation of Car and Boat from Vehicle is not virtual, which means that data member maxSpeed will not be unique in Amphibian.
Though inheritance (single or multiple) is one of the key features of object-oriented programming, one needs to be very careful when using it. The main thing to remember is the Principle of Substitutability, which says essentially that an object of a derived class should always be able to be used in place of (i.e., substituted for) an object of any ancestor class, whenever an object of that ancestor class was called for. Therefore the interface in a base class must not be limited in any way by a derived class. Some authors (Josuttis, for example) even recommend avoiding inheritance, using it only if something cannot be implemented in another way. The most frequent decision a designer has to make is whether something is-a-kind-of something else, or is-a-part-of that other thing. If the model permits the is-a-part-of approach, that is the preferred route to take. The is-a-part-of relationship is often called containment, or composition.