Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
Before C++, the most successful object-oriented language was
Smalltalk. Smalltalk was created from the ground up as an object-oriented
language. It is often referred to as pure, whereas C++ is called a hybrid
language because it supports multiple programming paradigms, not just the object-oriented paradigm. One of the design decisions made with Smalltalk was that all
classes would be derived in a single hierarchy, rooted in a single base class
(called Object this is the model for the object-based hierarchy). You cannot
create a new class in Smalltalk without deriving it from an existing class,
which is why it takes a certain amount of time to become productive in
Smalltalk: you must learn the class library before you can start making new
classes. The Smalltalk class hierarchy is therefore a single monolithic tree.
Classes in Smalltalk usually have a number of things in
common, and they always have some things in common (the characteristics
and behaviors of Object), so you don t often run into a situation where
you need to inherit from more than one base class. However, with C++ you can create as many distinct inheritance trees as you want. So for logical completeness the language
must be able to combine more than one class at a time thus the need for multiple
inheritance.
It was not obvious, however, that programmers required
multiple inheritance, and there was (and still is) a lot of disagreement about
whether it is essential in C++. MI was added in AT&T cfront release 2.0 in 1989 and was the first significant change to the language over
version 1.0. Since
then, a number of other features have been added to Standard C++ (notably
templates) that change the way we think about programming and place MI in a
much less important role. You can think of MI as a minor language feature
that is seldom involved in your daily design decisions.
One of the most pressing arguments for MI involved
containers. Suppose you want to create a container that everyone can easily
use. One approach is to use void* as the type inside the container. The
Smalltalk approach, however, is to make a container that holds Objects,
since Object is the base type of the Smalltalk hierarchy. Because
everything in Smalltalk is ultimately derived from Object, a container
that holds Objects can hold anything.
Now consider the situation in C++. Suppose vendor A
creates an object-based hierarchy that includes a useful set of containers
including one you want to use called Holder. Next you come across vendor
B s class hierarchy that contains some other class that is important to
you, a BitImage class, for example, that holds graphic images. The only
way to make a Holder of BitImages is to derive a new class from
both Object, so it can be held in the Holder, and BitImage:
This was seen as an important reason for MI, and a number of
class libraries were built on this model. However, as you saw in Chapter 5, the
addition of templates has changed the way containers are created, so this
situation is no longer a driving issue for MI.
The other reason you may need MI is related to design. You
can intentionally use MI to make a design more flexible or useful (or at least
seemingly so). An example of this is in the original iostream library
design (which still persists in today s template design, as you saw in Chapter
4):
Both istream and ostream are useful classes by
themselves, but they can also be derived from simultaneously by a class that
combines both their characteristics and behaviors. The class ios
provides what is common to all stream classes, and so in this case MI is a
code-factoring mechanism.
Regardless of what motivates you to use MI, it s harder to
use than it might appear.
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |