Multiple Inheritance

The previous examples in this chapter demonstrate “single inheritance,” where a class is derived from a single base class. C++ also supports “multiple inheritance,” where a class can be derived from more than one base class.

For example, suppose you wanted to declare a class SalesManager to describe employees who have characteristics of both the SalesPerson and Manager classes:

class SalesManager : public SalesPerson, public Manager

{

// ...

};

The SalesManager class inherits all the data members and member functions of SalesPerson and Manager.

You cannot specify a class as a direct base class more than once (for example,
you cannot enter Manager twice in the list of base classes). However, a class
can be an indirect base class more than once. For example, SalesManager has
Employee as an indirect base class twice: once through SalesPerson and once through Manager. As a result, each SalesManager object contains two copies of Employee's data members.

If a class acts as an indirect base class more than once, it is more complicated to call member functions defined by that base class. For example:

SalesManager aSellerBoss;

char *str;

str = aSellerBoss.getName(); // Error: ambiguous

The problem is that the compiler can't tell which copy of Employee should be used; since each copy's data members might contain different values, the value returned by getName depends on which copy is used. You must specify which copy you want, using the scope resolution operator:

str = aSellerBoss.Manager::getName();

This statement uses the name stored in the Manager's copy of Employee's data members.

The same ambiguity problem can arise even if an indirect base class is not repeated. If a base class (either direct or indirect) defines a member with the same name as a member defined in another base class, you must again use the scope resolution operator to specify whose member you want.

If a class acts as an indirect base more than once, there are also possible ambiguities when performing conversions between base and derived classes. For example, suppose you want to convert a pointer to a SalesManager into a pointer to an Employee:

Employee *empPtr;

SalesManager *salemgrPtr;

empPtr = salemgrPtr; // Error: ambiguous

Once again, the compiler can't tell which copy of Employee it should use for empPtr. To disambiguate, you must use a cast:

empPtr = (Manager *)salemgrPtr;

This statement converts salemgrPtr into a pointer to a Manager, and then converts that into a pointer to an Employee. As a result, empPtr points to Manager's copy of Employee's data members.

Since salesmanagers don't have two names and two social security numbers, the SalesManager class shouldn't contain two copies of Employee. To avoid this duplication, you can make Employee a “virtual base class.”

To do this, the classes that specify Employee as a direct base class must use the virtual keyword:

class WageEmployee : public virtual Employee

{

// ...

};

class Manager : public virtual Employee

{

// ...

};

Note that only WageEmployee and Manager need to use the virtual keyword. SalesPerson and SalesManager do not, because Employee is an indirect base class for them.

By making Employee a virtual base class, each instance of SalesManager now has only one copy of Employee's data members; there is no duplication. Any references to members defined by Employee are unambiguous, and so are conversions from a SalesManager pointer to a Employee pointer.

For a class like SalesManager, virtual base classes save space and allow a more accurate representation. However, virtual base classes impose a greater processing overhead. Consequently, you should use virtual base classes sparingly.

See Chapter 9, “Fundamentals of Object-Oriented Design,” for information on design issues surrounding multiple inheritance. See the C++ Language Reference for more information on single inheritance, virtual functions, multiple inheritance, and virtual base classes.