We define categories of rules:
- Design rules - recommendations that flatly proscribe or require a given practice without exception.
- Guidelines - suggested practices of a more abstract nature for which exceptions are sometimes legitimately made.
- Principles - certain observations and truths that have often proved useful during the design process but must be evaluated in the context of a specific design.
All of these rules are described in the [^1], readers are welcomed not to ask rationale here, but search through the book by themselves for answers.
All used terms are described in ./terms.md "terms.md".
- major design rule - It is almost always an error to place a definition with external linkage in a
.h
file.
5 int z; // illegal: external data definition
6 extern int LENGTH = 10; // illegal: external data definition
7 const int WIDTH = 5; // avoid: constant data definition
8 stati c int y; // avoid: static data definition
9 static void func() {...} // avoid: static function definition
11 static int s_count; // fine: static member declaration
12 static const double S__PI; // fine: static const member dec.
13 int d_size; // fine: member data definition
15 int size() const; // fine: member function declaration
16 }; // fine: class definition
18 inline int Radio::size() const {
20 } // fine: inline function definition
22 int Radio::s_count; // illegal: static member definition
24 double Radio::-S_PI = 3.14159265358; // illegal: static const member def,
25 int Radio::size() const { /*...*/ } // illegal: member function definition
- major design rule - avoid free functions with external linkage. The definitions to be avoided at file scope in
.c
files are data and functions that have not been declared static [^1: 1.1.4]
3 int i; // avoid: external linkage
4 int max(int a, int b){...} // avoid: external linkage
5 inline int min(){...} // fine: internal linkage
6 static int mean(){...} // fine: internal linkage
7 class Link; // fine: internal linkage
8 enum {...} // fine: internal linkage
9 const double PI = 3; // fine: internal linkage
10 static const char *names[] = {"a", "b"} // fine: internal linkage
11 typedef struct {..} mytype;// fine: does not introduce new type.
- major design rule - keep class data members private.
- major design rule - avoid free functions (except operator functions) at file scope in
.h
.
- major design rule - avoid enums, typedefs and constants at file scope in
.h
files.
- major design rule - avoid using preprocessor macros in header files except as include guards.
- major design rule - only classes, structures, unions and free operator functions should be declared at file scope in
.h
file. Only classes, structures , unions, and inline (member or free operator) functions should be defined at file scope in a .h
file.
- major design rule - place a unique and predictable (internal) include guard around the contents of each header file.
- guideline - document the public API so that they are usable by others. Have at least one other developer review each interface.
- guideline - explicitly state conditions under which behavior is undefined.
- principle - the use of
assert
statements can help to document the assumptions you make when implementing your code.
- principle - a component is the appropriate fundamental unit of design.
- major design rule - logical entities declared within a component should not be defined outside that component.
- minor design rule - the root names of the
.cpp
and the .hpp
file that comprise a component should match exactly.
- major design rule - the
.cpp
file of every component should include its own .hpp
file as the first substantive line of code.
- guideline - clients should include header files providing required type definitions directly; except for non-private inheritance, avoid relying on one header file to include another.
- major design rule - avoid definitions with external linkage in the
.cpp
file of a component that are not declared explicitly in the corresponding .hpp
file.
- major design rule - avoid accessing a definition with external linkage in another component via a local declaration; instead, include the
.hpp
file for that component.
- guideline - a component
X
should include y.hpp
only if X
makes direct substantive use of a class or free operator function defined in Y
.
principle - granting (local) friendship to classes defined within the same component does not violate encapsulation.
Example: defining an iterator class along with a container class in the same component enables user extensibility, improves maintainability and enhances reusability while preserving encapsulation.
- principle - all tests must be done in isolation. Testing a component in isolation is an effective way to ensure reliability.
- principle - every directed acyclic graph can be assigned unique level numbers; a graph with cycles cannot. A physical dependency graph that can be assigned unique level numbers is said to be levelizable.
- principle - in most real-world situations, large designs must be levelizable if they are able to be tested effectively.
- principle - testing only the functionality directly implemented within a component enables the complexity of the test to be proportional to the complexity of the component.
- guideline - avoid cyclic physical dependencies among dependencies.
- principle - factoring a concrete class into two classes containing higher and lower levels of functionality can facilitate levelization.
- principle - factoring an abstract base class into two classes - one defining a pure interface, the other defining its partial implementation - can facilitate levelization.
- principle - a protocol class can be used to eliminate both compile and link time dependencies.
- major design rule - prepend every global identifier with its package prefix.
- major design rule - avoid cyclic dependencies among packages.
- guideline - avoid declaring results returned by value from functions as
const
.
- minor design rule - never pass a user-defined type to a function by value.
- guideline - avoid using
short
in the interface; use int
instead.
- guideline - avoid using
unsigned
in the interface; use int
instead.
- guideline - explicitly declare (public or private) the constructor and assignment operator for any class defined in a header file.
- minor design rule - in every class that declares or is derived from a class that declares a virtual function, explicitly declare the destructor as the first virtual function in the class and define it out of line.
[^1]: "John Lakos - Large Scale C++ Software Design".