At the last agile roundtable, I picked up a copy of Effective C++: 55 Specific Ways to Improve Your Programs and Designs, 3rd edition, by Scott Meyers. There are some programming books that I read in chunks over time, like Design Patterns, or Refactoring and then there are books that I prefer to absorb in one big intellectual gulp. “Effective C++” is in the latter category.
At 272 pages and about 3/4 of an inch thick, “Effective C++” is not in the “tome” category of programming book. Yet its not exactly a “light read”, either. This book is organized as a series of 55 specific items about C++ described in a short essay and accompanying code examples. The items are clumped together into chapters around a theme for the items. You could read the items in any order on an as-needed basis if you were venturing into a new area for the first time, or you can read all the items in order. The chapters are titled as follows:
- Accustoming Yourself to C++
- Constructors, Destructors, and Assignment Operators
- Resource Management
- Designs and Declarations
- Inheritance and Object-Oriented Design
- Templates and Generic Programming
- Customizing new and delete
The specific items are as follows:
- View C++ as a federation of languages.
- Prefer consts, enums and inlines to #defines.
- Use const whenever possible.
- Make sure that objects are initialized before they’re used.
- Know what functions C++ silently writes and calls.
- Explicitly disallow the use of compiler-generated functions you do not want.
- Declare destructors virtual in polymorphic base classes.
- Prevent exceptions from leaving destructors.
- Never call virtual functions during construction or destruction.
- Have assignment operators return a reference to *this.
- Handle assignment to self in operator=.
- Copy all parts of an object.
- Use objects to manage resources.
- Think carefully about copying behavior in resource-managing classes.
- Provide access to raw resources in resource-managing classes.
- Use the same form in corresponding uses of new and delete.
- Store newed objects in smart pointers in standalone statements.
- Make interfaces easy to use correctly and hard to use incorrectly.
- Treat class design as type design.
- Prefer pass-by-reference-to-const to pass-by-value.
- Don’t try to return a reference when you must return an object.
- Declare data members private.
- Prefer non-member non-friend functions over member functions.
- Declare non-member functions when type conversions should apply to all parameters.
- Consider support for a non-throwing swap.
- Postpone variable definitions as long as possible.
- Minimize casting.
- Avoid returning “handles” to object internals.
- Strive for exception-safe code.
- Understand the ins and outs of inlining.
- Minimize compilation dependencies between files.
- Make sure public inheritance models “is-a”.
- Avoid hiding inherited names.
- Differentiate between inheritance of interface and inheritance of implementation.
- Consider alternatives to virtual functions.
- Never redefine an inherited non-virtual function.
- Never redefine a function’s inherited default parameter value.
- Model “has-a” or “is-implemented-in-terms-of” through composition.
- Use private inheritance judiciously.
- Use multiple inheritance judiciously.
- Understand implicit interfaces and compile-time polymorphism.
- Understand the two meanings of typename.
- Know how to access names in templatized base classes.
- Factor parameter-independent code out of templates.
- Use member function templates to accept “all compatible types”.
- Define non-member functions inside templates when type conversions are desired.
- Use traits classes for information about types.
- Be aware of template metaprogramming.
- Understand the behavior of the new-handler.
- Understand when it makes sense to replace new and delete.
- Adhere to convention when writing new and delete.
- Write placement delete if you write placement new.
- Pay attention to compiler warnings.
- Familiarize yourself with the standard library, including TR1.
- Familiarize yourself with Boost.
I was familiar in one way or another with most of these items, but its good to have them all in one place instead of sprinkled throughout other books. I particularly enjoyed the chapters on “Inheritance and Object-Oriented Design” and “Customizing new and delete”. I found the coverage of multiple inheritance and private inheritance — two features unique to C++ compared to languages like Java or C# — to be quite useful.
There were a few items that taught me new things about my old friend C++. Item 12: Copy all parts of an object reminded me that my copy constructors of derived classes need to call the copy constructor of the base class and that my assignment operators of derived classees need to call the assignment operators of the base class. This is one of those mistakes that is all too easy to make and when you read the item you think “well, duh, of course you have to take care of the base class!”, yet there is nothing in the compiler that will warn you about it if you don’t.
Item 17: Store newed objects in smart pointers in standalone statements points out one of those subtle ways that resource leaks can sneak into your application as a result of trying to cram too much into a single logical statement. I tend not to like gratuitous variable declarations in functions and methods, but this is a case to remember as it may look gratuitous at first glance but it really does serve a purpose.
Item 27: Minimize casting once again reminds us once again how casts are a code smell for C++ and are best avoided. Why, just today I was reviewing some code in a product where I work and found a horrid and fragile construction that compiled cleanly because the programmer used a C-style cast to sweep things under the rug. The code in question is what I call “working by accident instead of working by design”; any number of seemingly minor changes to the source will cause this code to fail and yet still compile cleanly, all thanks to the use of a C-style cast.
Overall I would recommend this book to anyone who programs in C++ on a regular basis. If you are doing simple C++ programming on an infrequent basis, you can probably get by without this book. Still, you will find that some of the mystery of C++ revealed by this book, even if you only program infrequently in C++. Item 1: View C++ as a federation of languages reminds us that C++ is much, much more than just an object-oriented version of C, or “C with classes”. The interesting work happening in Boost and template metaprogramming is an example of this: C++ supports many programming paradigms, while other languages support one (Pascal for procedural programming, C#/Java for object-oriented programming, etc.).