Previous Table of Contents Next


As late as 1997—15 years after its initial design—tens of thousands of copies of Cfront were in daily use.

6.3.3. Language Feature Details

The major additions to C with Classes introduced to produce C++ were

  Virtual functions
  Function name and operator overloading
  References
  Constants (const)
  User-controlled free-store memory control
  Improved type checking

In addition, the notion of call and return functions (section 6.2.4.8) was dropped due to lack of use, and many minor details were changed to produce a cleaner language.

6.3.3.1. Minor Changes to C++

The most visible minor change to C++ was the introduction of BCPL-style comments:

   int a; /* C-style explicitly terminated comment */
   int b; // BCPL-style comment terminated by end-of-line

Because both styles of comments are allowed, people can simply use the style they like best.

The name new function for constructors had been a source of confusion, so the name constructor was introduced (section 6.2.4.8).

In C with Classes, a dot was used to express membership of a class as well as express selection of a member of a particular object. This had been the cause of some minor confusion and could also be used to construct ambiguous examples. To alleviate this, :: was introduced to mean membership of class, and . was retained exclusively for membership of object.

I borrowed the Algol68 notion that a declaration can be introduced wherever it is needed (and not just at the top of some block). Thus, I enabled an initialize-only or single-assignment style of programming that is less error prone than traditional styles. This style is essential for references and constants that cannot be assigned and inherently more efficient for types where default initialization is expensive.

6.3.3.2. Virtual Functions

The most obvious new feature in C++ compared to C with Classes—and certainly the one that had the greatest impact on the style of programming you could use for the language—was virtual functions. The idea was borrowed from Simula and presented in a form that was intended to make a simple and efficient implementation easy. The rationale for virtual functions was presented in The C++ Programming Language (Stroustrup, 1986a) and What Is Object-Oriented Programming? (Stroustrup, 1986c). To emphasize the central role of virtual functions in C++ programming, I quote the latter (Stroustrup, 1986c) in detail here:

An abstract data type defines a sort of black box. Once it has been defined, it does not really interact with the rest of the program. There is no way of adapting it to new uses except by modifying its definition. This can lead to severe inflexibility. Consider defining a type shape for use in a graphics system. Assume for the moment that the system has to support circles, triangles, and squares. Assume also that you have some classes:

   class point{ /* ... */ };
   class color{ /* ... */ };

You might define a shape like this:

   enum kind { circle, triangle, square };

   class shape {
       point center;
       color col;
       kind k;
       // representation of shape
   public:
       point where() { return center; }
       void move(point to) { center = to; draw(); }
       void draw();
       void rotate(int);
       // more operations
   };

The type field k is necessary to allow operations such as draw() and rotate() to determine what kind of shape they are dealing with (in a Pascal-like language, one might use a variant record with tag k). The function draw() might be defined like this:

   void shape::draw()
   {
       switch (k) {
       case circle:
           // draw a circle
           break;
       case triangle:
           // draw a triangle
           break;
       case square:
           // draw a square
       }
   }

This is a mess. Functions such as draw() must know about all the kinds of shapes there are. Therefore, the code for any such function grows each time a new shape is added to the system. If you define a new shape, every operation on a shape must be examined and (possibly) modified. You are not able to add a new shape to a system unless you have access to the source code for every operation. Because adding a new shape involves touching the code of every important operation on shapes, it requires great skill and potentially introduces bugs into the code handling other (older) shapes. The choice of representation of particular shapes can get severely cramped by the requirement that (at least some of) their representation must fit into the typically fixed-size framework presented by the definition of the general type shape.

The problem is that there is no distinction between the general properties of any shape (a shape has a color, it can be drawn, and so on) and the properties of a specific shape (a circle is a shape that has a radius, is drawn by a circle-drawing function, and so on). Expressing this distinction and taking advantage of it define object-oriented programming. A language with constructs that allows this distinction to be expressed and used supports object-oriented programming; other languages don’t.

The Simula inheritance mechanism provides a solution. First, specify a class that defines the general properties of all shapes:

   class shape {
       point center;
       color col;
       // ...
   public:
       point where() { return center; }
       void move(point to) { center = to; draw(); }
       virtual void draw();
       virtual void rotate(int);
       // ...
   };

The functions for which the calling interface can be defined, but where the implementation cannot be defined except for a specific shape, have been marked “virtual” (the Simula and C++ term for “may be redefined later in a class derived from this one”). Given this definition, we can write general functions manipulating shapes:

   void rotate_all(shape** v, int size, int angle)
   // rotate all members of vector “v” of size    “size” “angle”
      degrees
   {
       for (int i = 0; i < size; i++) v[i]->rotate(angle);
   }

To define a particular shape, we must say that it is a shape and specify its particular properties (including the virtual functions):

   class circle : public shape {
       int radius;
   public:
       void draw() { /* ... */ };
       void rotate(int) {}    // yes, the null function
   };

In C++, class circle is said to be derived from class shape, and class shape is said to be a base of class circle. An alternative terminology calls circle and shape subclass and superclass, respectively.


Previous Table of Contents Next