The Design of C++ Standard Components

Inheritance: why we have mostly avoided it

C++ supports object-oriented programming with two language features. Inheritance allows programmers to define new classes as subtypes of existing classes, and virtual functions allow programmers to defer binding of member functions until runtime.

However, C++'s support of object-oriented programming does not mean that every collection of C++ components must be fully object-oriented. One of the major problems with fully object-oriented libraries (despite their elegance) is the adverse impact on efficiency. For customers who are primarily concerned with efficiency, it is more important to have components which can be used to produce fast, compact code that compiles quickly. This is particularly important for basic building blocks, as found in C++ Standard Components.

Thus, our library is not a monolithic ``class library'' of the kind popularized by the Smalltalk language, and mimicked by the NIH Class Library. Rather, it is a collection of loosely-coupled components which can be regarded as extending the built-in types of C++.

As a consequence, we take a minimalist approach to inheritance. We use it only when it makes our components more efficient, or when it solves certain problems in the type system (see below).

We do not intend for our components to serve as a collection of base classes that users extend via derivation. It is exceedingly difficult to make a class extensible in abstracto, and even harder when one is trying to provide classes that are as efficient as possible in every sense. Contrary to a common misconception, it is rarely possible for a programmer to take an arbitrary class and derive a new, useful, and correct subtype from it, unless that subtype is of a very specific kind anticipated by the designer of the base class. Classes can only be made extensible in certain directions, where each of those directions is consciously chosen (and programmed in) by the designer of the class. Class libraries which claim to be ``fully extensible'' are making an extravagant claim which frequently does not hold up in practice.

Given the above, we do not make all member functions virtual as insurance against possible derivation by the client. Making all member functions virtual is at best pointless and at worst inefficient and dangerous. It is pointless, because full virtuality does not imply extensibility; extensibility is much harder to achieve than that. It is inefficient because of the virtual inlining problem (see the section ``Avoiding language features with execution speed penalties'' in this topic). It is dangerous because in general, only certain functions will have to be overridden by derived classes. Making functions virtual that are not specifically intended (by the base class designer) to be overridden, increases the odds of a derived class accidentally overriding a base class function.

For those classes which we do not intend to be extended by derivation (which is most of the classes in the library) we have simply implemented the class as efficiently as possible. There is absolutely no reason to sacrifice efficiency for an elusive kind of ``extensibility.''

Next topic: Two uses of inheritance
Previous topic: Minimizing the number of static objects defined in headers

© 2005 The SCO Group, Inc. All rights reserved.
SCO OpenServer Release 6.0.0 -- 02 June 2005