Why C++ Robert C. Martin Object Mentor Associates rmartin@oma.com 708.918.1004 4 Apr 1995 Abstract C++ is a language that seems to stimulate a tremendous amount of debate and religious furor. This paper attempts to cut through this "nonsense" and make a case for why C++ is a reasonable language for developing object oriented applications. Introduction. Why is there so much controversy about C++? Probably because it has enjoyed so much success. It is easy to believe that if C++ had remained a backwater language, the voices of its detractors would not be so loud. However, C++ *has* succeeded in a huge segment of the industry, and many proponents of other languages are quite vocal in bemoaning this fact. But what are they really complaining about? Ian Joyner, one of the most vocal and prolific C++ detractors, has published and widely distributed his notorious "C++?? A Critique of C++, Second Edition" (write to ian@syacus.acus.oz.au for a copy). This is a generally well written (although in my opinion misguided) paper, and I recommend it to anyone considering C++ as a language. In this paper Ian says: "...the C base is C++'s greatest weakness." And this is a common thread through many of the arguments against C++. They say that C++ is a bad language because C was a bad language. I don't think that C needs a defense. The last several decades have proven it to be an extremely flexible and productive language, suitable for building applications of nearly every kind. However, in the early days of C, there were many detractors who decried the ugliness of the language and promoted "pure" languages like Pascal. The current debate regarding C++ and other languages such as Eiffel, Obj-C and Smalltalk seems similar. Why did C succeed? Because it cut through all the crap. Instead of fighting for "purity", the authors of C created a language that could be *used*. They never contended that it was perfect or pure, certainly it is not. They wanted a simple, moderately low level language that they and other people could use. In that goal, they succeeded. Why has C++ succeeded? For the same reason. It cuts through all the nonsense. Instead of trying for some lofty goal of "purity", Stroustrup has created a language that can be *used*. None of the users or authors of C++ claim that the language is perfect, or pure. However the claims of its usability have been confirmed over and over again. The language *is being used* more than any other language in its class. And that usage is increasing at a geometric rate. This paper is not an attempt to answer the complaints made by C++ detractors. Rather it presents arguments for why C++ is a reasonable choice for the implementation of object oriented designs in a wide variety of application environments. Who am I that I should write such a paper? I am a user of C++, and the manager of a team of users. I have been using C++ since 1989, and have been involved with object oriented technology since 1986. I have produced, several large and successful systems in C++, and continue to enjoy such success. I am an international consultant who works with engineering teams, training them in object oriented design and C++, and helping them with their software designs. I am also the author of: "Designing Object Oriented C++ Applications using the Booch Method" by Robert Martin, Prentice Hall, 1995, ISBN 0-13-203837-4 What the Deuce is Purity? Almost everyone can agree that C++ is not a "pure" OO language. However, this definition is problematic in that it implies that "purity" is a valid concept. Yet, there is no accepted definition of the term "pure OO language". Despite years of discussion and argument on the net, and in other circles, I have never seen anyone present a definition of a "pure OOPL" that met with general agreement by others. The fact that C++ is "impure" is often taken to mean that users are free to leave the golden path to OO and "backslide" into the unrepentant state of (gasp) procedural design. However, *every* OOPL must allow such "backsliding", because object oriented design is a *superset* of procedural design. It is *always* possible for a procedural design to be implemented in an object oriented language. This assertion is easy to prove. A procedural design is a set of data structures and procedures. In an OOPL, one can create a single object which contains all the data structures and procedures of the procedural design. Thus, the procedural design is implemented within the confines of a single object. Don't laugh! I have talked to engineers who have done just this. So what good is purity? I can't tell you, because I can't define it. I can tell you this, however. Purity, if it exists, does not force users to write good programs. And impurity, if it exists, does not force programmers to write bad programs. The main (possibley sole) ingredient required for good programs is good programmers. The Benefits of C++ as a "Better C". Ignoring Object Oriented programming for the moment, probably the biggest benefit that C++ provides is type safety. C++ is a type safe language. What does this mean? Simply speaking it means this: void f(long l) {...} // function declared in one file. f(1); // in C, this can cause stack corruption since an integer // is passed instead of a long. However in C++, the // compiler knows that 'f' wants a "long" and so promotes // the '1' to the correct type. ANSI C makes type safety optional. C++ makes it mandatory. In C++ it is very difficult (not impossible) to violate the type system. Why is this important? Because type errors in C are often the causes of strange bugs that take weeks or months to find, and that exhibit transient and misleading behavior. They often foul the stack or heap and cause eventual failure several million instructions after the precipitating event. Such bugs are the hallmark of poor quality software. C++, in a single stroke, eliminates a huge percentage of them. This advantage alone makes C++ a better alternative than C. C programmers don't have to learn any of the other features of the language to take advantage of type safety. C++ is a Low Level Language with High Level Features. When all is said and done, C++ is a language for the manipulation of bits and bytes. Although "purists" might rant and bemoan this attribute of C++, *engineers* sigh with relief. All computer applications are, eventually, the manipulation of bits and bytes. We might not wish this were so, but it is. And C++ provides the features that allow low level manipulations to be effient and easy. If, in the end, I need to store the bit pattern '10110100' in the byte whose address is FCA9DE, C++ will allow me to do so, without complaint. But C++ also provides many high level features, so that the user can "pretend" that C++ is a high level language. This makes C++ useful in those places where a high level language is needed. As a user, and the representative of other users, I can attest to the fact that C++ succeeds in this dual role. I can implement high level abstractions in C++ using the high level structures of the language, and I can implement low level details using the low level structures of the language. It all works, and works quite well. Object Oriented Programming. Without a doubt, this feature of C++ is the most important. The reason is that well structured object oriented designs create applications that are robust, maintainable and comprised of elements that can be reused in other applications. It must be stressed that the use of C++ does not guarantee that applications will have these desirable attributes. In fact, C++ does not even "nudge" the user in the right direction. And neither does any other language! Bad designs are BAD DESIGNS, period. No language will correct them. No language, regardless of its "purity" will improve a bad design. An Object Oriented design is a difficult thing to create. Using an OO programming language does not guarantee that the design will be object oriented. In fact, the design should be expressible indepedently of the language. It is for this reason that Object Oriented design notations (e.g. Booch, or OMT) have been developed. There is a large contingent of developers who believe that object oriented design is simply the partitioning of the application into representative objects. Such designs can be represented in the design notations, and can be implemented in OO programming languages. However, most often these designs do not follow the principles of object oriented design. Object oriented designs should have certain attributes. Those attributes include the structuring of source code dependencies such that source code modules that implement the detailed parts of the application are isolated from the source code modules that implement the high level policies, or business rules, of the application. Moreover, the detailed modules should depend upon the high level modules, and the high level modules MUST NOT depend upon any of the detailed modules. This management of dependencies is the key design activity needed to produce robust, maintainable and reusable designs. And yet very many designers that are new to OO do not understand this. No language, regardless of its OO "purity" will help a designer manage the dependencies between high level details and low level policies. -- [ I have written a paper that describes ] -- [ dependency management, and it is also well ] -- [ covered in my book. If you would like a copy ] -- [ of the paper, send me some email and I'll ] -- [ send it to you. rmartin@oma.com ] Once an object oriented design has been created, C++ is an excellent language in which to implement it. Once implemented, all the advantages of the design are exhibited by the source code. The modules created are robust in that they are isolated from changes made in other modules, they are maintainable in that the engineers need not fear that changes in one module will ripple to other modules, and they are reusable in that the high level modules do not depend upon the low level details, and so can be reused in a different detailed context. Run Time Efficiency. There are two broad classifications of Object Oriented languages: Statically typed languages like C++ and Eiffel, and dynamically typed language like Smalltalk or Objective-C. Both of these language types employ "run time binding" which is the attribute that makes object oriented design possible. However, the mechanisms employed by the two different language types are very different. In dynamically typed languages, every run time binding is looked up in a table of some kind. Although table lookups can be very fast, they still involve a loop, and a token matching algorithm. Hash tables are common, and some calls can even be resolved at compile time. Generally, however, it is impossible to precisely describe the time required for such an operation. And generally the operation requires dozens, or even hundreds of microseconds. Since a well designed object oriented program will make the majority of its function calls via this mechanism, the runtime overhead of a dynamic type system can be significant. In statically typed languages, run time bindings are performed by indexing into a table of pointers. This operation always requires exactly the same amount of time, and can be theoretically reduced to a single index into an array followed by a single pointer dereference. On most machines of today's calibre, these operations require less than a microsecond total. Most implementations of C++ come very close to achieving the theoretical limit, adding only one or two other pointer dereferences or index operations. For each implementation, the time required can be exactly specified. Thus, run time efficiency of run time bindings in statically typed language is always better than that of dynamically typed languages. What requires one or two microseconds in C++ may require dozens or hundreds of microseconds in a dynamically typed language. This makes C++ an excellent choice for applications that have intense real time requirements. It also makes C++ a good choice for component libraries that *might* be used in intense real time applications. Memory Managment. C++ is one of the very few OO programming language that does not require dynamic memory management. Perfectly sound OO programs can be implemented without using the "heap" at all. This is extremely important to those industries that have regulations against "heap" usage. (e.g. Avionics.) Objects can be created on the stack just like auto variables, or they can be created statically. In such cases, there is no need for memory cleanup, and no risk of heap fragmentation or exhaustion. C++ is the one of the only languages that allows the programmer to have complete control over the way memory is managed. If the user wants to use a heap, he can. If he wants to implement his own heap, he can. If he wants some objects to be created on the heap, and other objects to be created on a separately managed heap, he can. If he wants his heap to be garbage collected, he can implement or buy the appropriate collector. I know of no other language that provides such flexibility. C++ is often "bashed" because it does not provide garbage collection. The argument proferred is often: "GC is intrinsic to OO. GC is needed to free the programmer from managing memory. The lack of GC imposes extra complexity on the design." Irrespective of the truth or falsity of these arguments, there is another argument to be made: "Programmers should have complete control over how memory is managed." I know of no language that provides more control over this area than C++. The point is this. If you really think that you need garbage collection, then you can have it. It won't be neatly integrated into the language, and there will be a certain amount of difficulty involved, however you can gain the bulk of the advantages of a garbage collected environment if you absolutely need them. However, if you don't need them, (and very often you don't) you don't have to have them. In this, C++ is unique. But what about memory leaks? Yes, this is a problem. Any consciencious programmer should deal with this issue. There are several ways. First, you can get a garbage collector. Second, you can use a tool like Purify, or Bounds Checker. Third, you can use an environment like ObjectCenter for tracking heap problems. Fourth, you can write your own heap allocator and put your own tracing mechanisms in. Fifth, you don't need to use the heap at all. OK, then what about heap fragmentation? Yes, this too is a problem. However it is almost trivial to write an allocator that can't be fragmented. Stroustrup describes such an allocator in "The C++ Programming Language" 2d. ed. And Rogue Wave sells one in the Booch Component library. C++ allows you to use such an allocator instead of the standard (malloc based) allocator. Third Party Sources and Support. C++ is by far the most supported object oriented language. There are compilers for nearly every major platform out there. There are even cross compilers for embedded systems. Moreover there is a large and growing supply of third party tools and class libraries. No other object oriented language even comes close to the coverage and sourcing that C++ enjoys. C++ derives from C. Although this point is often counted as an argument against C++, I count it as an advantage of the language. It means that C++ programs can be easily integrated with legacy C code, and legacy code in other languages that are C callable. C++ programs can call C programs, and C programs can call C++ programs. Moreover, C++ inherits all of the attributes that make C such a successful and useful language. Although C++ is a bigger and more complex language than C, it still "feels" like C. This is a subjective point, but one that is not insignificant. Conclusion Wait a minute! Is that all? Aren't there more reasons to use C++ than that? Sure. There are lots of little details that I passed over. Why? Because they are details, and because they can be contentious. I could tell you how great templates are, or how wonderful operator overloading is, or how neat pure virtual functions are. But why? For every point I could make, someone else could refute it and say it is ugly or nasty. And who should you believe? Your best recourse is to find out about these features for yourself. I recommend that you do a lot of reading. Then examine the arguments, pro and con, and decide for yourself. Don't hesitate to drop me a line asking me what I think about this or that. (rmartin@oma.com). But isn't C++ ugly? Part of it, yes. So what? English is an ugly language too. Ask any Frenchman. Consider the word "garage". This is an ugly word. Listen to it. Yuck! But we don't often see the "ugliness" of the word because we *use* it to represent a concept that is not ugly at all. The same is true of C++. Yes, I can understand why some people would think that some of the features or syntax of C++ are ugly. But the concepts underneath are not. Once you begin using the language, the "ugliness" evaporates. I have not been trying to tell you that C++ is the only OOPL and all wisdom dies with it. I have not been trying to tell you that C++ is the language you should be using. I have simply been trying to tell you why I use it, and why I think it is a reasonable implementation language for object oriented designs. If you want to use some other language, do so. It's no skin off my apple. But don't fall prey to the religious arguments of C++ detractors. C++ is a great language. It will not corrupt you, nor drag you down into the hell reserved for mediocre programmers. Nor will it turn you into "Super-Programmer" able to leap tall specifications with a single switch/case statement. No language has such powers! Software is not a religion. C++ is a language. It is a good language. It is a usable language. And if you are interested in implementing object oriented designs, it will work for you.