Exceptional C++
Herb Sutter

Guidelines

  1. Never dereference an invalid iterator. [3]
  2. Prefer passing objects by const& instead of by value. [18, 71]
  3. Prefer precomputing values that won't change, instead of recreating objects unnecessarily. [18]
  4. For consistency, always implement post-increment in terms of pre-increment, otherwise your users get surprising (and often unpleasant) results. [19, 74]
  5. Prefer pre-increment. Only use post-increment if you're going to use the original value. [19]
  6. Watch out for hidden temporaries created by implicit conversions. One good way to avoid this is to make constructors explicit when possible and avoid writing conversion operators. [19, 71]
  7. Be aware of object lifetimes. Never, ever, ever return pointers or references to local automatic objects; they are completely unuseful because the calling code can't follow them, and (what's worse) the calling code might. [21]
  8. Reuse code - especially standard library code - instead of handcrafting your own. It's faster, easier, and safer. [23, 70]
  9. If a function isn't going to handle (or translate or deliberately absorb) an exception, it should allow the exception to propagate up to a caller who can handle it. [28]
  10. Always structure your code so that resources are correctly freed and data is in a consistent state even in the presence of exceptions. [28]
  11. Prefer cohesion. Always endeavor to give each piece of code - each module, each class, each function - a single well-defined responsibility. [36, 44, 67, 85]
  12. Always use the "initialization is resource acquisition" idiom to isolate resource ownership and management. [46, 58]
  13. Design with reuse in mind. [54]
  14. Always be exception-aware. Know what code might emit exceptions. [63]
  15. Prefer writing "a op= b" instead of "a = a op b". It's clearer, and it's often more efficient. [71]
  16. If you supply a standalone version of an operator (for example operator+), always supply an assignment version of the same operator (operator+=) and prefer implementing the former in terms of the latter. Also, always preserve the natural relationship between op and op=. [72]
  17. Prefer the following guidelines for making an operator a member versus a non-member: [72]
  18. Always return stream references from operator>> and operator<<. [73]
  19. Make base class destructors virtual (unless you are certain that no one will ever attempt to delete a derived object through a pointer to base). [77]
  20. When providing a function with the same name as inherited functions, be sure to bring the inherited functions into scope with a using declaration if you don't want to hide them. [78]
  21. Never change the default parameters of overriden inherited functions. [78]
  22. Never inherit publicly to reuse code (in the base class); inherit publicly in order to be reused (by code that uses base objects polymorphically). [81, 96]
  23. When modeling "is implemented in terms of", always prefer membership/aggregation, not inheritance. Use private inheritance only when inheritance is absolutely necessary - that is, when you need access to protected members or you need to override a virtual function. Never use public inheritance for code reuse. [82]
  24. Avoid public virtual functions; prefer using the Template Method pattern instead. [84]
  25. For widely used classes, prefer to use the compiler-firewall idiom (Pimpl idiom) to hide implementation details. Use an opaque pointer (a pointer to a declared but undefined class) declared as struct XxxImpl* pimpl; to store private members (including both state variables and member functions) - for example, class Map { private: struct MapImpl* pimpl; };. [85, 105, 110]
  26. Prefer aggregation (aka composition, layering, "has a", delegation) to inheritance. When modeling "is implemented in terms of", always prefer expressing it using aggregation, not inheritance. [90]
  27. Always ensure that public inheritance models both "is a" and "works like a" according to the Liskov Substitution Principle. All overridden member functions must require no more and promise no less. [96]
  28. Never #include unnecessary header files. [100]
  29. Prefer to #include <iosfwd> when a forward declaration of a stream will suffice. [101]
  30. Never #include a header when a forward declaration will suffice. [102]
  31. Never inherit when composition is sufficient. [108]
  32. Avoid inlining or detailed tuing until performance profiles prove the need. [116, 191]
  33. Use namespaces wisely. If you put a class into a namespace, be sure to put all helper functions and operators into the same namespace, too. If you don't, you may discover surprising effects in your code. [140]
  34. Understand the five major distinct memory stores, why they're different, and how they behave: stack (automatic variables); free store (new, delete); heap (malloc, free); global scope (statics, global variables, file scope variables, and so forth); const data (string literals, and so forth). [143]
  35. Prefer using the free store (new, delete). Avoid using the heap (malloc, free). [143]
  36. Always provide both class-specific new (or new[]) and class-specific delete (or delete[]) if you provide either. [146, 150]
  37. Always explicitly declare operator new() and operator delete() as static functions. They are never non-static member functions. [147, 148]
  38. Never treat arrays polymorphically. [147]
  39. Prefer using vector<> or deque<> instead of arrays. [148]
  40. Never write a copy assignment operator that relies on a check for self-assignment in order to work properly; an exception-safe copy assignment operator is automatically safe for self-assignment. [160]
  41. It's all right to use a self-assignment check as an optimization to avoid needless work. [160]
  42. Avoid writing conversion operators. Avoid non-explicit constructors. [163]
  43. Always endeavor to write exception-safe code. Always structure code so that resources are correctly freed and data is in a consistent state even in the presence of exceptions. [164, 168, 197]
  44. Avoid the "dusty corners" of a language; use the simplest techniques that are effective. [165, 168, 191]
  45. Avoid unnecessarily terse or clever code, even if it's perfectly clear to you when you first write it. [168]
  46. Prefer providing a nonthrowing Swap() and implement copy assignment in terms of copy construction as follows: [170]
        // GOOD
        T& T::operator=( const T& other ) {
           T temp( other );
           Swap( temp );
           return *this;
        }
    
  47. Never use the trick of implementing copy assignment in terms of copy construction by using an explicit destructor followed by placement new, even though this trick crops up every three months on the newsgroups. Never write - [171]
        // BAD
        T& T::operator=( const T& other ) {
           if (this != &other) {
              this->~T();             // evil
              new (this) T( other );  // evil
           }
           return *this;
        }
    
  48. Prefer using the form "T t(u);" instead of "T t = u;" where possible. The former usually works wherever the latter works, and has other advantages - for example, it can take multiple parameters. [174]
  49. Avoid declaring const pass-by-value function parameters. [176]
  50. When using return-by-value for non-builtin return types, prefer returning a const value. [177]
  51. Prefer new-style casts. Only dynamic_cast() is not equivalent to a C-style cast. All other new-style casts have old-style equivalents. [183]
  52. Avoid casting away const. Use mutable instead. [185]
  53. Avoid downcasts. [186]
  54. Prefer passing objects by reference instead of by value, using const wherever possible. [191]
  55. Avoid using global or static objects. If you must use a global or static object, always be very careful about the order-of-initialization rules. [194]
  56. Always list base classes in a constructor's initialization list in the same order in which they appear in the class definition. [196]
  57. Always list the data members in a constructor's initialization list in the same order in which they appear in the class definition. [196]

Common mistakes

  1. Never make exception safety an afterthought. Exception safety affects a class's design. It is never "just an implementation detail." [17, 35]
  2. "Exception-unsafe" and "poor design" go hand in hand. If a piece of code isn't exception-safe, that's generally okay and can simply be fixed. But if a piece of code cannot be made exception-safe because of its underlying design, that almost always is a signal of its poor design. Example 1: a function with two different responsibilities is difficult to make exception-safe. Example 2: a copy assignment operator that has to check for self-assignment cannot be exception-safe. [37]
  3. Never use public inheritance except to model true Liskov "is a" and "works like a". All overridden member functions must require no more and promise no less. [81]
  4. "T t(u);" is always initialization; it is never assignment, and so it never calls T::operator=(). It is just a syntax holdover from C, not an assignment operation. [174]
  5. Never write code that depends on the order of evaluation of function arguments. [198, 199]

Canonical exception-safety rules

  1. Never allow an exception to escape from a destructor or from an overloaded operator delete or operator delete[]. [29, 57, 58]
  2. In each function, take all code that might emit an exception and do all that work safely off to the side. Only then, when you know that the real work has succeeded, should you modify the program state (and clean up) using only non-throwing operations. [32, 34, 47, 58]