Guidelines for Physical Design



  1. Document the interfaces so that they are usable by others; have at least one other developer review each interface.

  2. Explicitly state conditions under which behavior is undefined.

  3. Be consistent about indentifier names; use either uppercase or underscore but not both to delimit words in identfiers.

  4. Be consistent about names used in the same way; in particular adopt consistent method names and operators for recurring design patterns such as iteration.

  5. Clients should include header files providing required type definitions directly; except for non-private inheritance, avoid relying on one header file to include another.

  6. A component x should include y.h only if x makes direct substantive use of a class or free operator function defined in y.

  7. Avoid granting (long-distance) friendship to a logical entity defined in another component.

  8. Avoid cyclic physical dependencies among components.

  9. In general, avoid granting any one component license that, if also taken by other components, would adversely impact the system as a whole.

  10. Prefer modules to non-local static instances of objects, especially when:

  11. The semantics of an overloaded operator should be natural, obvious, and intuitive to clients.

  12. The syntactic properties of overloaded operators for user-defined types should mirror the properties already defined for the fundamental types.

  13. Avoid hiding a base-class function in a derived class.

  14. Every object in a system should be const-correct.

  15. Think twice (at least) before casting away const.

  16. For functions that return an error status, an integral value of 0 should always mean success.

  17. Functions that answer a yes-or-no question should be worded appropriately (e.g., isValid) and return an int value of either 0 ("no") or 1 ("yes").

  18. Avoid declaring results returned by value from functions as const.

  19. Avoid default arguments that require the construction of an unnamed temporary object.

  20. Be consistent about returning values through arguments (e.g., avoid declaring non-const reference parameters).

  21. Avoid storing the address of any argument to a function in a location that will persist after the function terminates; pass the address of the argument instead.

  22. Whenever a parameter passes its argument by reference or pointer to a function that neither modifies that argument nor stores its writeable address, that parameter should be declared const.

  23. Avoid declaring parameters passed by value to a function as const.

  24. Consider placing parameters (except perhaps those with default arguments) that enable modifiable access before parameters that pass arguments by value, const reference, or const pointer.

  25. Avoid granting friendship to individual functions.

  26. Avoid declaring a function inline whose body produces object code that is larger than the object code produced by the equivalent non-inline function call itself.

  27. Avoid declaring a function inline that your compiler will not generate inline.

  28. Avoid using short in the interface; use int instead.

  29. Avoid using unsigned in the interface; use int instead.

  30. Avoid using long in the interface; assert(sizeof(int) >= 4) and use either int or a user-defined large-integer type instead.

  31. Consider using double exclusively for floating-point types used in the interface unless there is a compelling reason to use float or long double.

  32. Consider avoiding "cast" operators, especially to fundamental integral types; instead make the conversion explicit.

  33. Explicitly declare (either public or private) the constructor and assignment operator for any class defined in a header file, even when the default implementations are adequate.

  34. In classes that do not otherwise declare virtual functions, explicitly declare the destructor as non-virtual and define it appropriately (either inline or out-of-line).

  35. Use short instead of int in the implementation as an optimization only when it is known to be safe to do so.

  36. Consider not using unsigned even in the implementation.

  37. When designing a function, component, package, or entire system, use the simplest techniques that are effective.

  38. Avoid allowing programmatic access to physical values.

  39. The result of calling a const member function should not alter any programmatically accessible value in the object.

  40. Prefer object-specific over class-specific memory management.

  41. Use a non-const pointer data member to hold managed objects.

  42. Consider providing a way to switch between block allocations and individual allocations of dynamic memory.

  43. When implementing memory management for a completely general, parameterized container that manages the memory for its contained objects, assume that the parameterized type defines a copy constructor and a destructor -- nothing more.

  44. Where possible, implement templates on top of a factored reusable void * pointer type using inline functions to reestablish type safety.