Previous | Table of Contents | Next |
Basically, I rejected all forms of dynamic resolution beyond the use of virtual functions as unsuitable for a statically typed language under severe efficiency constraints. Maybe I should at this point have revived the notion of call and return functions (section 6.2.4.8) to mimic the CLOS :before and :after methods. However, people were already worrying about the complexity of the multiple inheritance mechanisms, and I am always reluctant to reopen old wounds.
Multiple inheritance in C++ became controversial (Cargill, 1991; Carroll, 1991; Sakkinen, 1992; Waldo, 1991) for several reasons. The arguments against it centered on the real and imaginary complexity of the concept, the utility of the concept, and the impact of multiple inheritance on other extensions and tool building. In addition, proponents of multiple inheritance can and do argue over exactly what multiple inheritance is supposed to be and how it is best supported in a language. I thinkas I did thenthat the fundamental flaw in these arguments is that they take multiple inheritance far too seriously. Multiple inheritance doesnt solve all your problems, but it doesnt need to because it is quite cheap, and sometimes it is very convenient to have. Grady Booch (1991) expressed a slightly stronger sentiment: Multiple inheritance is like a parachute; you dont need it very often, but when you do, it is essential.
Over the years, the use of multiple inheritance has grown steadily and a number of programming techniques that rely on it have evolved. By now (1998), I consider multiple inheritance important for any language that has both static type checking and inheritance.
The very last feature added to 2.0 before it shipped was abstract classes. Late modifications to releases are never popular, and late changes to the definition of what will be shipped are even less so. I remember that several members of management thought I had lost contact with the real world when I insisted on this feature.
A common complaint about C++ was (and is) that private data is visible and that when private data is changed, code using that class must be recompiled. Often, this complaint is expressed as abstract types in C++ arent really abstract. What I hadnt realized was that many people thought that because they could put the representation of an object in the private section of a class declaration, they actually had to put it there. This is clearly wrong (and that is how I failed to spot the problem for years). If you dont want a representation in a class, thus making the class an interface only, you simply delay the specification of the representation to some derived class and define only virtual functions. For example, you can define a set of T pointers like this:
class set { public: virtual void insert(T*); virtual void remove(T*); virtual int is_member(T*); virtual T* first(); virtual T* next(); virtual ~set() { } };
This provides all the information people need to use set except that whoever actually creates the set must know something about how some particular kind of set is represented. For example, given the following, we can create slist_set objects that can be used as sets by users that have never heard of a slist_set:
class slist_set : public set, private slist { slink* current_elem; public: void insert(T*); void remove(T*); int is_member(T*); T* first(); T* next(); slist_set() : slist(), current_elem(0) { } };
The only problem was that in C++ as defined before 2.0, there was no explicit way of saying The set class is just an interface: Its functions need not be defined, it is an error to create objects of class set, and anyone who derives a class from set must define the virtual functions specified in set. Release 2.0 allowed a class to be declared explicitly abstract by declaring one or more of its virtual functions pure using the syntax =0:
class set { // abstract class public: virtual void insert(T*) = 0; // pure virtual function virtual void remove(T*) = 0; // ... };
The =0 syntax wasnt exactly brilliant, but it expresses the desired notion of a pure virtual function in a way that is terse and fits the use of 0 to mean nothing or not there in C and C++. The alternative, introducing a new keyword such as pure, wasnt an option. Given the opposition to abstract classes as a late and unimportant change, I would never simultaneously have overcome the traditional, strong, widespread, and emotional opposition to new keywords in parts of the C and C++ community.
The importance of the abstract class concept is that it allows a cleaner separation between a user and an implementor than is possible without it. This limits the amount of recompilation necessary after a change and also the amount of information necessary to compile an average piece of code. By decreasing the coupling between a user and an implementor, abstract classes provide an answer to people complaining about long compile times and also serve library providers who must worry about the impact to users of changes to a library implementation. I had unsuccessfully tried to explain these notions in The C++ Programming Language (Stroustrup, 1986a). With an explicit language feature supporting abstract classes, I was much more successful (Stroustrup, 1991).
Sometime in 1988, it became clear that C++ would eventually have to be standardized (Stroustrup, 1989). There was now a handful of independent implementations in use or being produced, and clearly an effort had to be made to write a more precise and comprehensive definition of the language and also to gain wide acceptance for that definition. At first, formal standardization wasnt considered an option. Many people involved with C++ consideredand still considerstandardization abhorrent before genuine experience is gained. However, making an improved reference manual wasnt something that could be done by one person (me) in private. Input and feedback from the C++ community were needed. Thus I came upon the idea of rewriting the C++ reference manual and circulating its draft among important and insightful members of the C++ community worldwide.
Previous | Table of Contents | Next |