Contents Up << >>

What are some advantages of C++?

GROWTH OF C++: C++ is by far the most popular OOPL. The number of C++ users is doubling every 7.5 to 9 months. Knowing C++ is a good resume-stuffer (but use it as an OOPL rather than just as a better C).

ENCAPSULATION: Hiding our data structures allows us to change one chunk of a system without breaking other chunks. We provide our software chunks (we call them "classes") with safe interfaces. Users of a chunk use its interface only. The relatively volatile "implementation" of this interface is encapsulated ("put into a capsule") to prevent users from becoming reliant on its temporary decisions. In simple C, this was done by making a module's data "static", thus preventing another module from accessing our module's bits.

MULTIPLE INSTANCES: The typical C solution to encapsulation (see above) doesn't support multiple instances of the data (it's hard to make multiple instances of a module's "static" data). If we needed multiple instances in C, we used a "struct" (but this doesn't support "encapsulation"). In C++, we can have both multiple instances and encapsulation via a "class": the "public" part of a class contains the class's interface (normally these are a special kind of function called a "member function"), and the "private" part of a class contains the class's implementation (typically these are where the bits live).

INLINE FUNCTION CALLS: In straight C, you can achieve "encapsulated structs" by putting a void* in a struct (the access functions use pointer casts). This forfeits type safety, and also imposes a function call to access even trivial fields of the struct (if you allowed direct access to the struct's fields, the underlying data structure would be difficult to change since too many chunks would rely on it being the "old" way). Function call overhead is small, but can add up. C++ classes allow function calls to be expanded "inline," so you have: the 1) safety of encapsulation, 2) convenience of multiple instances, 3) speed of direct access. Furthermore the parameter types of these inline functions are checked by the compiler, an improvement over C's #define macros.

OVERLOADING OPERATORS: C++ lets you overload the standard operators on a class, which lets users exploit their intuition (e.g., "myString + yourString" might do string concatenation, "myDate++" might increment the date, "z1 * z2" might multiply complex numbers z1 and z2, "a[i]" might access the "i"th element of the "linked list" called "a", etc. You can even have "smart pointers" that could "point" to a disk record or wherever ("x = *p" could "dereference" such a pointer, which could seek to the location on disk where p "points" and return its value). This allows users to program in the language of the problem domain rather than in the language of the machine.

INHERITANCE: We still have just scratched the surface. In fact, we haven't even gotten to the "object-oriented" part yet! Suppose you have a Stack data type with operations push, pop, etc. Suppose you want an InvertableStack, which is "just like" Stack except it also has an "invert" operation. In "C" style, you'd have to either (1) modify the existing Stack module (trouble if "Stack" is being used by others), or (2) copy Stack into another file and text edit that file (results in lots of code duplication, another chance to break something tricky in the Stack part of InvertableStack, and especially twice as much code to maintain). C++ provides a much cleaner solution: inheritance. You say "InvertableStack inherits everything from Stack, and InvertableStack adds the invert operation." Done. Stack itself remains "closed" (untouched, unmodified), and InvertableStack doesn't duplicate the code for push/pop/etc.

POLYMORPHISM AND DYNAMIC BINDING: The real power of OOP isn't just inheritance, but is the ability to pass an InvertableStack around as if it actually were a Stack. This is "safe" since (in C++ at least) the is-a relation follows public inheritance (i.e., a InvertableStack is-a Stack that can also invert itself). Polymorphism and dynamic binding are easiest to understand from an example, so here's a "classic": a graphical draw package might deal with Circles, Squares, Rectangles, general Polygons, and Lines. All of these are Shapes. Most of the draw package's functions need a "Shape" parameter (as opposed to some particular kind of shape like Square). E.g., if a Shape is picked by a mouse, the Shape might get dragged across the screen and placed into a new location. Polymorphism and dynamic binding allow the code to work correctly even if the compiler knows only that the parameter is a "Shape" without knowing the exact kind of Shape it is. Furthermore suppose the "pick_and_drag(Shape*)" function just mentioned was compiled on Tuesday, and on Wednesday you decide to add the Hexagon shape. Strange as it sounds, pick_and_drag() will still work with Hexagons, even though the Hexagon didn't even exist when pick_and_drag() was compiled!! (It's not really "amazing" once you understand how the C++ compiler does it -- but it's still very convenient!)