C:\1980S\CPP.TXT
1985
C++: Abstraction with a Performance Budget
Bjarne Stroustrup combined classes and systems programming under constraints that made implementation cost part of the language's identity.
A program can be fast because its author has managed every detail personally. It can also be difficult to change for exactly the same reason. Once a large project contains many representations, shared assumptions, and resource lifetimes, keeping all of them in everybody’s head becomes an expensive optimization.
C++ pursued a useful combination: give programmers stronger ways to organize a system while retaining control over its implementation costs. Classes could express the program’s concepts. The underlying machinery still had to be suitable for real systems work.
The tension is central to its history. An abstraction should help the programmer, but it shouldn’t demand an extravagant runtime merely to exist. That sounds entirely reasonable until one tries to design the language in which all the competing requirements must coexist.
Before the plus signs
Bjarne Stroustrup began the work that became C++ at Bell Labs in 1979. Its initial form was C with Classes. His earlier experience with Simula influenced the organizational features he wanted, while C supplied a practical systems programming foundation.1
The name C++ arrived in late 1983, suggested by Rick Mascitti. Internal use preceded commercial availability. The first commercial implementation and the first edition of The C++ Programming Language appeared in October 1985. These are distinct milestones: the beginning of development, the naming of the language, and its commercial release shouldn’t collapse into one birthday.2
The early implementation known as cfront translated C++ into C, which an existing C compiler could then compile. That provided a practical route onto machines with established toolchains. The extra language features therefore had to become constructs that real compilation systems could handle, rather than remain attractive ideas on a whiteboard.1
The language continued to evolve. Templates, exception handling, and the eventual standard library should not be smuggled into an account of the original commercial release. The first ISO C++ standard came in 1998. The 1980s story is about an earlier language growing under production use and implementation constraints.2

Bjarne Stroustrup, at left, at CEE-SECR in Moscow, October 15, 2010. Photograph by Julia Kryuchkova, source, CC BY-SA 2.5. Unaltered later photograph, not an image from the language’s launch.
A class gives a representation a boundary
Consider an invented program that records a count of available tickets. In plain procedural code, the count might be an integer that many parts of the program can change. Every caller must remember the rules: never go negative, reject an excessive reservation, and keep the meaning of “available” consistent.
A class can put that representation behind operations. Here is an original teaching example using a conservative subset of C++:
class TicketPool {
int available;
public:
TicketPool(int initial) {
available = initial;
}
int remaining() const {
return available;
}
int reserve(int amount) {
if (amount < 0 || amount > available)
return 0;
available -= amount;
return 1;
}
};
The field available is private because it precedes the public: section. Outside code uses the public operations instead of changing the field directly. The constructor sets an object’s initial state. The const member operation promises not to modify that object’s ordinary stored state when reporting its count.
Start with TicketPool tickets(10). Reserving three succeeds and leaves seven. Requesting eight then fails and leaves seven. Requesting negative two fails as well. The operation preserves its rule by checking the proposed change before committing it.
There is a deliberate remaining limitation: the constructor assumes a nonnegative starting count. Its interface should document or enforce that precondition. A small example becomes more useful when it admits where its guarantee starts. Classes don’t automatically make every path into an object valid; their design supplies the guarantee.
The compiler helps maintain the agreement
The immediate benefit is local reasoning. A reviewer can inspect the operations that modify available, instead of searching a whole program for assignments to a shared integer. If all modifications go through reserve, the rule has a manageable home.
The compiler can reject ordinary external attempts to access a private field. This turns a convention into something the language can help enforce. The programmer still chooses the representation and operations; the language makes their boundary visible and checkable.
Our class doesn’t need an elaborate object runtime to keep that integer private. Access control is largely a compile-time concern. The compiler checks permitted use and generates ordinary executable operations. This illustrates why some useful abstraction can have little direct runtime cost.
A member function also operates on a particular object. Conceptually, an implementation can pass the object’s address to code that accesses its fields. The exact generated layout and calling sequence depend on the compiler; the important teaching point is that object-oriented syntax doesn’t require a second computer hiding behind each object.
That control mattered to C++‘s aims. Stroustrup’s historical account emphasizes organization combined with systems efficiency. The implementation had to pay for itself in practical programs, where performance, integration with existing code, and tool availability constrained what users could adopt.1
The benefit isn’t that every use of a class is free. Operations still perform work, and some language mechanisms add indirection or storage. The useful question is which costs a particular design requires and whether those costs purchase something worthwhile.
Different objects can share an operation
Inheritance and virtual functions address another organizational problem. Suppose a drawing program has circles and rectangles, both of which can draw themselves. Code organizing a list of shapes should not need to repeat a growing list of type tests every time it asks an object to draw.
A common interface can provide a drawing operation, and derived types can supply implementations. A virtual call chooses the appropriate operation for the actual object. This is a teaching explanation of a long-standing C++ mechanism, not code for a surviving 1985 drawing application.
A typical implementation uses a table of function addresses and a way to locate the appropriate table for an object. An indirect call then selects the function. Such tables are an implementation technique, not a promise that every compiler must use identical layout or produce identical instructions.
The cost purchases flexibility at a useful point. The drawing loop can deal with the shared interface while each shape knows its own geometry. Adding another kind of shape needn’t require rewriting all the places that invoke the common operation.
It also creates design obligations. The interface must be meaningful across the objects that share it. Inheritance used merely to borrow a convenient field can muddle the relationship. The compiler can implement a hierarchy that makes little conceptual sense with exemplary obedience.
Original explanatory diagram, 2026; a simplified account of cfront’s translation route, not the output of a recovered historical compiler.
Lifetime is part of the design
An object doesn’t only have operations; it has a lifetime. C++ constructors and destructors provide points at which setup and cleanup can be associated with that lifetime. A resource-owning object can acquire something when created and release it when destroyed.
Think of a file. If many program branches open it, use it, and close it manually, every branch must remember cleanup. Organizing ownership around an object gives cleanup a more definite home. The details depend on the supported language facilities and the resource wrapper’s implementation, but the organizational aim is clear.
This doesn’t amount to automatic garbage collection. Nor does it eliminate invalid pointers, ownership mistakes, or resource misuse. C++ retained substantial low-level freedom. An abstraction boundary is useful because it can concentrate that freedom where it is needed and expose safer operations elsewhere.
That is a demanding bargain. The author can control representations and costs, but has to reason carefully about what owns which resource and how long it remains valid. A language can make certain responsibilities expressible without making them disappear.
The 1980s translation route also kept the language near existing systems. Reusing a C compiler reduced one barrier to implementation availability. It did not make all C++ source valid C or guarantee that every C program had unchanged meaning as C++. Evolution preserved substantial continuity while introducing distinctions developers had to learn.
What the example can actually promise
A reservation operation also needs a policy for failure. Our example returns zero without changing the count, so the caller can report the problem or choose another amount. That policy is part of the interface. Silently reducing an excessive request to whatever remains would preserve a nonnegative count while surprising the caller about what had actually been reserved. Representation safety and useful behavior have to be designed together.
The abstraction has to earn its keep
Our ticket class illustrates a modest success. It gives the count a meaning, concentrates its modifications, and rejects two invalid reservations. It doesn’t need inheritance, a template library, or a framework large enough to issue its own documentation policy. The appropriate abstraction is the one that clarifies this program’s responsibilities.
That is the part of early C++ I find most instructive. The language’s organizational tools had to coexist with practical deployment and performance constraints. A clever feature wasn’t sufficient; its implementation and the programmer’s ability to understand its cost mattered too.
The result was a language whose history contains both expressive ambition and continuing arguments about complexity. Those arguments follow naturally from the project it undertook. C++ made abstraction something a systems programmer could use while still asking where the data lived, which function would run, and what the machine had to pay for it.
Footnotes
-
Bjarne Stroustrup, A History of C++: 1979–1991, creator’s detailed history, particularly C with Classes, implementation constraints, and translation. Historical paraphrase kept compact; worked example and cost discussion are original teaching explanations. ↩ ↩2 ↩3
-
Stroustrup’s FAQ, “When was C++ invented?”, internal use, naming, October 1985 implementation/book release, and later features; publication list, first-edition book and contemporary papers. ↩ ↩2