Previous Table of Contents Next


6.3.3.6. Memory Management

Long before the first C with Classes program was written, I knew that free store (dynamic memory) would be used more heavily in a language with classes than in traditional C programs. This was the reason for the introduction of the new and delete operators in C with Classes. The new operator that both allocates memory from the free store and invokes a constructor to ensure initialization was borrowed from Simula. The delete operator was a necessary complement because I did not want C with Classes to depend on a garbage collector (section 6.8.2.1). The reasons for that included poor experience with the overhead of garbage collectors, a need to coexist with programs written in C, use of C with Classes in low-level and performance-critical software (such as device drivers), and use of C with Classes in systems where predictability of service was important (such as real-time control software).

The argument for the new operator can be summarized like this. Would you rather write this:

   X* p = new X(2);

or this:

   struct X * p = (struct X *) malloc(sizeof(struct X));
   if (p == 0) error(“memory exhausted”);
   p->init(2);

In which version are you most likely to make a mistake? The arguments against—which were voiced quite a lot at the time—were “but we don’t really need it,” and “but someone will have used new as an identifier.” Both observations are correct, of course.

Introducing operator new thus made the use of free store more convenient and less error prone. This increased its use even further so that the C free-store allocation routine malloc() used to implement new became the most common performance bottleneck in real systems. This was no real surprise either; the only problem was what to do about it. Having real programs spend 50% or more of their time in malloc() wasn’t acceptable.

I found per-class allocators and deallocators very effective. The fundamental idea is that free-store memory usage is dominated by the allocation and deallocation of a lot of small objects from very few classes. Take over the allocation of those objects in a separate allocator, and you can save both time and space for those objects and also reduce the amount of fragmentation of the general free store. The mechanism provided for 1.0, “assignment to this,” was too low level and error prone and was replaced by a cleaner solution in Release 2.0 (section 6.4.1).

Note that static and automatic (stack allocated) objects were always possible and that the most effective memory-management techniques relied heavily on such objects. The string class was a typical example: Here String objects are typically on the stack so that they require no explicit memory management, and the free store they rely on is managed exclusively and invisibly to the user by the String member functions.

6.3.3.7. Type Checking

The C++ type-checking rules were the result of experiments with C with Classes. All function calls are checked at compile time. The checking of trailing arguments can be suppressed by explicit specification in a function declaration. This is essential to allow C’s printf():

   int printf(const char* ...); // accept any argument
                                // after the initial character string

   // ...

   printf(“date: %s %d 19%d\n”,month,day,year); // maybe right

Several mechanisms were provided to alleviate the withdrawal symptoms that many C programmers feel when they first experience strict checking. Overriding type checking using the ellipsis was the most drastic and least recommended of those. Function name overloading (section 6.3.3.3) and default arguments (Stroustrup, 1986a) made it possible to give the appearance of a single function taking a variety of argument lists without compromising type safety. The stream I/O system demonstrates that the weak checking wasn’t necessary even for I/O (see section 6.5.3.1).

6.3.4. C++’s Relationship to Classic C

With the introduction of a separate name, C++, and the writing of a C++ reference manual (Stroustrup, 1984a), compatibility with C became an issue of major importance and a point of controversy.

Also, in late 1983, the branch of Bell Labs that developed and supported UNIX and produced AT&T’s 3B series of computers became interested in C++ to the point where it was willing to put resources into the development of C++ tools. Such development was necessary for the evolution of C++ from a one-man show to a language on which a corporation could base critical projects. Unfortunately, it also implied that development management needed to consider C++.

The first demand to emerge from development management was that of 100% compatibility with C. The ideal of C compatibility is quite obvious and reasonable, but the reality of programming isn’t that simple. For starters, with which C should C++ be compatible? C dialects abounded, and although ANSI C was emerging, it was still years from having a stable definition, and its definition allowed many dialects. Naturally, the average user who wanted C compatibility insisted that C++ should be compatible with the local C dialect. This was an important practical problem and a great concern to me and my friends. It seemed far less of a concern to business-oriented managers and salespeople who either didn’t quite understand the technical details or wanted to use C++ to tie users into their software or hardware.

Another side of the compatibility issue was more critical: “In which ways must C++ differ from C to meet its fundamental goals?” and also “In which ways must C++ be compatible with C to meet its fundamental goals?” Both sides of the issue are important and revisions were made in both directions during the transition from C with Classes to C++ as shipped as Release 1.0. Slowly and painfully, an agreement emerged that there would be no gratuitous incompatibilities between C++ and ANSI C (when it became a standard; Stroustrup, 1986a), but that there was such a thing as an incompatibility that was not gratuitous. Naturally, the concept of “gratuitous incompatibilities” was a topic of much debate and took up a disproportional part of my time and effort. This principle has lately been known as “C++: As Close to C as Possible—But No Closer,” after the title of a paper by Andrew Koenig and me (Koenig & Stroustrup, 1989a).


Previous Table of Contents Next