Rogue Wave banner
Previous fileTop of documentContentsIndexNext file

18.5 Multiple Inheritance

In Section 15, we built a Bus class by inheriting from RWCollectable. If we had an existing Bus class at hand, we might have saved ourselves some work by using multiple inheritance to create a new class with the functionality of both Bus and RWCollectable as follows:

class CollectableBus : public RWCollectable, public Bus {
  .
  .
  .
};

This is the approach taken by many of the Rogue Wave collectable classes; for example, class RWCollectableString inherits from both class RWCollectable and class RWCString. The general idea is to create your object first, then tack on the RWCollectable class to make the whole thing collectable. This way, you will be able to use your objects for other things or in other situations, where you might not want to inherit from class RWCollectable.

There is another good reason for using this approach: to avoid ambiguous base classes. Here's an example:

class A { };

class B : public A { };

class C : public A { };

class D : public B, public C { };

void fun(A&);

main () {
  D  d;
  fun(d);                                           // Which A ?
}

There are two approaches to disambiguating the call to fun(). We can either change it to:

fun((B)d);                         // We mean B's occurrence of A

or make A a virtual base class.

The first approach is error-prone because the user must know the details of the inheritance tree in order to make the proper cast.

The second approach, making A a virtual base class, solves this problem, but introduces another: it becomes nearly impossible to make a cast back to the derived class! This is because there are now two or more paths back through the inheritance hierarchy or, if you prefer a more physical reason, the compiler implements virtual base classes as pointers to the base class and you can't follow a pointer backwards.

We could exhaustively search all possible paths in the object's inheritance hierarchy, looking for a match. (This is the approach of the NIH Classes.) However, this search is slow, even if speeded up by "memorizing" the resulting addresses, since it must be done for every cast. Since it is also bulky and always complicated, we decided that it was unacceptable.

Hence, we went back to the first route. This can be made acceptable if we keep the inheritance trees simple by not making everything derive from the same base class. Hence, rather than using a large secular base class with lots of functionality, we have chosen to tease out the separate bits of functionality into separate, smaller base classes.

The idea is to first build your object, then tack on the base class that will supply the functionality you need, such as collectability. You thus avoid multiple base classes of the same type and the resulting ambiguous calls.



Previous fileTop of documentContentsIndexNext file
©Copyright 1999, Rogue Wave Software, Inc.
Send mail to report errors or comment on the documentation.