Previous Table of Contents Next


6.4.1. C++ 2.0 Feature Overview

The main features of 2.0 were first presented in “The Evolution of C++: 1985-1987” (Stroustrup, 1987a) and summarized in the revised version of that paper (Stroustrup, 1989b), which accompanied 2.0 as part of its documentation:

  Multiple inheritance
  Type-safe linkage
  Better resolution of overloaded functions
  Recursive definition of assignment and initialization
  Better facilities for user-defined memory management
  Abstract classes
  Static member functions
  const member functions
  protected members (first provided in Release 1.2)
  Overloading of operator ->
  Pointers to members (first provided in Release 1.2)

Most of these extensions and refinements represented experience gained with C++ and couldn’t have been added earlier without more foresight than I possessed. Naturally, integrating these features involved significant work, but it was most unfortunate that this was allowed to take priority over the completion of the language as outlined in the “What Is?” paper.

Most features enhanced the safety of the language in some way or other. Cfront 2.0 checked the consistency of function types across separate compilation units (type-safe linkage), made the overload resolution rules order independent, and also ensured that more calls were considered ambiguous. The notion of const was made more comprehensive, pointers to members closed a loophole in the type system, and explicit class-specific memory allocation and deallocation operations were provided to make the error-prone “assignment to this” technique redundant.

To some people, the most important feature of Release 2.0 wasn’t a feature at all but a simple space optimization. From the beginning, the code generated by Cfront tended to be pretty good. As late as 1992, Cfront generated the fastest-running code in a benchmark used to evaluate C++ compilers on a Sparc. There have been no significant improvements in Cfront’s code generation since Release 1.0. However, Release 1.* was wasteful because each compilation unit generated its own set of virtual function tables for all the classes used in that unit. This could lead to megabytes of waste. At the time (about 1984), I considered the waste necessary in the absence of linker support and asked for such linker support. By 1987, that linker support hadn’t materialized. Consequently, I re-thought the problem and solved it by the simple heuristic of laying down the virtual function table of a class right next to its first non-virtual non-inline function.

6.4.2. Multiple Inheritance

In many people’s minds, multiple inheritance—the ability to have two or more direct base classes—is the feature of 2.0. I disagreed at the time because I felt that the sum of the improvements to the type system was of far greater practical importance. Also, adding multiple inheritance in 2.0 was a mistake. Multiple inheritance belongs in C++ but is far less important than parameterized types. As it happened, parameterized types in the form of templates appeared only in Release 3.0. There were a couple of reasons for choosing to work on multiple inheritance at the time: The design was further advanced and the implementation could be done within Cfront. Another factor was purely irrational: Nobody doubted that I could implement templates efficiently. Multiple inheritance, on the other hand, was widely supposed to be very difficult to implement efficiently. Thus, multiple inheritance seemed more of a challenge, and because I had considered it as early as 1982 and found a simple and efficient implementation technique in 1984, I couldn’t resist the challenge. I suspect that this is the only case where fashion affected the sequence of events.

In September 1984, I presented the C++ operator overloading mechanism at the IFIP WG2.4 conference in Canterbury (Stroustrup, 1984c). There, I met Stein Krogdahl from the University of Oslo, who was just finishing a proposal for adding multiple inheritance to Simula (Krogdahl, 1984). His ideas became the basis for the implementation of ordinary multiple base classes in C++. He and I later found out that the proposal was almost identical to an idea for providing multiple inheritance in Simula that had been considered by Ole-Johan Dahl in 1966 and rejected because it would have complicated the Simula garbage collector (personal communication, 1988).

The original and fundamental reason for considering multiple inheritance was simply to allow two classes to be combined into one in such a way that objects of the resulting class would behave as objects of either base class (Stroustrup, 1986c):

A fairly standard example of the use of multiple inheritance would be to provide two library classes displayed and task for representing objects under the control of a display manager and co-routines under the control of a scheduler, respectively. A programmer could then create classes such as

   class my_displayed_task : public displayed, public task {
       // ...
   };

   class my_task : public task {  // not displayed
       // ...
   };

   class my_displayed : public displayed  {   // not a task
       // ...
   };

Using (only) single inheritance, only two of these three choices would be open to the programmer.

The implementation requires little more than remembering the relative offsets of the task and displayed objects in a my_displayed_task object. All the gory implementation details were explained in “Multiple Inheritance for C++” (Stroustrup, 1987b). In addition, the language design must specify how ambiguities are handled and what to do if a class is specified as a base class more than once in a derived class (Stroustrup, 1987b):

Ambiguities are handled at compile time:

   class A { public: void f(); /* ... */ };
   class B { public: void f(); /* ... */ };
   class C : public A, public B {  /* no f() ... */ };
   void g()
   {
       C* p;
       p->f();    // error: ambiguous
   }

In this, C++ differs from the object-oriented Lisp dialects that support multiple inheritance.


Previous Table of Contents Next