- 11.1. Constructors must initialize all data members in the initialization list, and in the order they're declared in the class definition.
- 11.2. Class member functions which return a pointer or reference to a data member, must return it as "const T*" or "const T&".
- 11.3. A class function must not deallocate memory that was allocated outside of the class implementation file.
- 11.4. A class destructor must deallocate all of the memory allocated for data members of the instance.
- 11.5. Function arguments which are pointer, array, or reference types, which are not changed by the function, must be declared "const".
- 11.6. A function must contain at most one "return" statement.
- 11.7. Do not use "goto" or "continue".
- 11.8. Only use "break" in switch statements.
- 11.9. Declare variables at the latest possible location.
- 11.10. Use assert() to check for programming or interface errors.
- 11.11. Functions with "true/false" semantics must return "bool".
- 11.12. Declare default arguments in the .hpp file, not the .cpp file.
- 11.13. Pass objects (especially containers) by (const) reference, not value.
- 11.14. Return objects (especially containers) by value, unless it makes sense to return a const reference.
- 11.15. Exceptions must be caught by reference, not by value or pointer.
- 11.16. When re-throwing an exception, use "throw;", not "throw ex;".
- 11.17. Destructors must catch all exceptions and throw none.
- 11.18. Exception specifications must not be used.
- 11.18.1. Exception to 11.18: a destructor which catches all exceptions can be marked with "throw()".
The next three -- 11.6 - 11.8 -- impose structured-programming doctrine, which says that
goto
statements impede automated reasoning about functions (break
, continue
, and out-of-place return
are synonyms for goto
). Not many people are doing proofs of program correctness, but good structure also aids human reasoning about programs, as well as human debugging. Occasionally you can make a case for simplified functions by using an early return statement; or sometimes a goto is needed to improve loop performance. Make those be rare exceptions.Passing objects by reference (11.13) avoids copy construction during function calls, which is usually a timesaver -- though you might pass by value if your first action inside the function is to make a copy anyway. Of course if an argument is to be modified within the function, the correct choice is a non-const reference. For return values (11.14), it's almost always right to return an object instead of a reference. You might be tempted to return const references for some trivial "get" members, but even then, if the internal data layout of the class changes sometime in the future, you'll have a lot of work to do chasing down the function calls because the return type will change. Better to just start off returning by value.
Finally, there are some things to keep in mind about exceptions. Scott Meyers points out that catching by value leaves you susceptible to slicing off data in polymorphic classes; catching by pointer brings up confusing issues of ownership and deallocation. Catching by reference is the only solution. For a similar reason, a simple "throw;" is preferred to throwing the exception by name: fewer chances to make a mistake or accidentally involve a copy constructor. As for exception specifications, it's tricky to get them correct, and they add little value. The best solution is to only allow them for the situation where we absolutely expect no exceptions: in a destructor.
Next: the final chapter! Stylistic issues.
No comments:
Post a Comment