Coding Style
Team LiB
Previous Section Next Section

Coding Style

14. Prefer compile- and link-time errors to run-time errors.

Don't put off 'til run time what you can do at build time: Prefer to write code that uses the compiler to check for invariants during compilation, instead of checking them at run time. Run-time checks are control- and data-dependent, which means you'll seldom know whether they are exhaustive. In contrast, compile-time checking is not control- or data-dependent and typically offers higher degrees of confidence.

15. Use const proactively.

const is your friend: Immutable values are easier to understand, track, and reason about, so prefer constants over variables wherever it is sensible and make const your default choice when you define a value: It's safe, it's checked at compile time (see Item 14), and it's integrated with C++'s type system. Don't cast away const except to call a const-incorrect function (see Item 94).

16. Avoid macros.

TO_PUT_IT_BLUNTLY: Macros are the bluntest instrument of C and C++'s abstraction facilities, ravenous wolves in functions' clothing, hard to tame, marching to their own beat all over your scopes. Avoid them.

17. Avoid magic numbers.

Programming isn't magic, so don't incant it: Avoid spelling literal constants like 42 or 3.14159 in code. They are not self-explanatory and complicate maintenance by adding a hard-to-detect form of duplication. Use symbolic names and expressions instead, such as width * aspectRatio.

18. Declare variables as locally as possible.

Avoid scope bloat, as with requirements so too with variables): Variables introduce state, and you should have to deal with as little state as possible, with lifetimes as short as possible. This is a specific case of Item 10 that deserves its own treatment.

19. Always initialize variables.

Start with a clean slate: Uninitialized variables are a common source of bugs in C and C++ programs. Avoid such bugs by being disciplined about cleaning memory before you use it; initialize variables upon definition.

20. Avoid long functions. Avoid deep nesting.

Short is better than long, flat is better than deep: Excessively long functions and nested code blocks are often caused by failing to give one function one cohesive responsibility (see Item 5), and both are usually solved by better refactoring.

21. Avoid initialization dependencies across compilation units.

Keep (initialization) order: Namespace-level objects in different compilation units should never depend on each other for initialization, because their initialization order is undefined. Doing otherwise causes headaches ranging from mysterious crashes when you make small changes in your project to severe nonportability even to new releases of the same compiler.

22. Minimize definitional dependencies. Avoid cyclic dependencies.

Don't be over-dependent: Don't #include a definition when a forward declaration will do.

Don't be co-dependent: Cyclic dependencies occur when two modules depend directly or indirectly on one another. A module is a cohesive unit of release (see page 103); modules that are interdependent are not really individual modules, but super-glued together into what's really a larger module, a larger unit of release.

Thus, cyclic dependencies work against modularity and are a bane of large projects. Avoid them.

23. Make header files self-sufficient.

Behave responsibly: Ensure that each header you write is compilable standalone, by having it include any headers its contents depend upon.

24. Always write internal #include guards. Never write external #include guards.

Wear head(er) protection: Prevent unintended multiple inclusions by using #include guards with unique names for all of your header files.

    Team LiB
    Previous Section Next Section