10.2.5. Multiple Inheritance

Time: 00:03:01 | Download: Large, Large (CC), Small | Streaming (CC) | Slides (PDF)

Inheritance is the second requirement for membership in the object-oriented club - but full membership only requires single inheritance. Programming languages supporting multiple inheritance allow a subclass or child to have more than one superclass or parent. Multiple inheritance is neither required by the object-oriented paradigm nor universally supported by object-oriented programming languages. C++'s support for multiple inheritance has always been controversial.

A UML class diagram with three classes: Person, List, and PersonList. PersonList inherits from both Person and List.
class Person
{
};

class List
{
};

class PersonList
    : public Person, public List
{
};
An abstract representation of a PersonList object in memory. A PersonList object consists of two embedded objects. At the top, an instance of Person followed by an instance of List. The bottom part is unique to and comes from PersonList.
(a)(b)(c)
Multiple Inheritance. Initially, multiple inheritance allowed C++ programmers to create general, reusable container classes. The features needed to store and organize data into specific data structures (e.g., lists and trees) are independent of the stored data. Programmers would subclass a container class like List and a data class like Person, creating a class that could organize itself into a list of objects storing information about people.
  1. A UML class diagram implementing multiple inheritance. Person is an application class, List is a basic (library) data structure or container, and PersonList is a class that can organize the attributes and operations of a Person into a list structure.
  2. C++ implements multiple inheritance with a comma-separated list of superclasses.
  3. The abstract representation of multiple inheritance extends the abstract relationship of single inheritance. An instance of PersonList has instances of Person and List. The bottom rectangle represents the features defined in PersonList.
Templates have rendered this use of multiple inheritance obsolete. They provide a simple, elegant solution for implementing container class libraries. Nevertheless, programmers still use multiple inheritance to implement input and output streams and has some utility in creating classes for exception handling.
The UML class diagram of three classes in a multiple inheritance relationship. Each class has two or three member functions but no member variables.

class Person
+f() : void
+h(s : string) : void
+display() : void

class List
+g() : void
+h(i : int) : void
+display() : void

class PersonList : public Person, public List
+function() : void
+display() : void
void PersonList::function()
{
    public:
        f();			// (a)
        g();

        h("Dilbert");		// (b)
        h(100);
}

void PersonList::display()
{
    public:
        Person::display();	// (c)
        List::display();
}
Using multiple inheritance. Subclasses with two or more superclasses inherit functions from all their superclasses, which leads to three cases that we must consider:
  1. The superclass function names are relatively unique - no two superclasses have functions with the same name. There is no ambiguity when subclass objects call a superclass function.
  2. Two or more superclasses have functions with the same name but different parameters. The argument type determines which function subclass objects call.
  3. Two or more superclasses have a function with the same name and parameters. The subclass must disambiguate calls to the superclass functions with the superclass name and scope resolution operator.
Four classes arranged in the 'deadly diamond' pattern. Class A has two subclasses: B and C (i.e., classes B and C both inherit from A). Classes B and C both have class D as a subclass (i.e., class D inherits from both B and C). So, there is more than one inheritance path from D to A. Abstract representations of objects instantiated from classes B, C, and D. The object instantiated from class B embeds an instance of A; the object instantiated from class C also embeds an instance of A. The object instantiated from class D embeds objects from both B and C, which means that it embeds two subobjects from class A, one each from B and C.
(a)(b)
The deadly diamond. A fourth case involving multiple inheritance causes a problem we can't easily fix. The deadly diamond illustrates the ambiguities multiple inheritance causes. They result from multiple paths from a subclass to a superclass.
  1. The UML class diagram of the deadly diamond.
  2. An abstract representation of the deadly diamond in main memory. Instances of classes B and C each contain an instance of class A. An instance of class D contains instances of classes B and C, each of which has an instance of class A. When D uses a feature inherited from A, which instance of A supplies the feature - the one inherited through B or the one inherited through C?
Solving the ambiguities stemming from multiple inheritance paths to a single superclass requires virtual inheritance, which the text does not cover.