Development guide


Unless otherwise stated, the following rules also apply to member functions.

Function arguments

Do not use unspecified function arguments (ellipsis notation).
Avoid const pass-by-value function parameters in function declarations. They are pointless.
Avoid functions with too many (rule of thumb: more than five) arguments. Functions having long argument lists look complicated, are difficult to read, and can indicate poor design. In addition, they are difficult to use and to maintain.
Prefer references to pointers. Use a pointer only if the argument could be null or if using a reference would always require dereferencing a pointer when calling the function.
Use constant references (const &) instead of call-by-value, unless using a pre-defined data type or a pointer.

By using references instead of pointers as function arguments, code can be made more readable, especially within the function. A disadvantage is that it is not easy to see which functions change the values of their arguments. Member functions storing pointers provided as arguments should document this clearly by declaring the argument as a pointer instead of as a reference. This simplifies the code, since it is normal to store a pointer member as a reference to an object.

There are no null references in C++. This means that an object must have been allocated before passing it to a function. The advantage of this is that it is not necessary to test the existence of the object within the function.

C++ invokes functions using call-by-value semantics. This means that the function arguments are copied to the stack via invocations of copy constructors, which, for large objects, reduces performance. In addition, destructors will be invoked when exiting the function. const & arguments mean that only a reference to the object in question is placed on the stack (call-by-reference) and that the object's state (its instance variables) cannot be modified. (At least some const member functions are necessary for such objects to be at all useful.)

Function overloading

When overloading functions, all variations should have the same semantics (be used for the same purpose).

Overloading of functions can be a powerful tool for creating a family of related functions that only differ as to the type of data provided as arguments. If not used properly (such as using functions with the same name for different purposes), they can, however, cause considerable confusion. Template functions can be a good alternative to overloading.

Formal arguments

The names of formal arguments to functions must be specified and must be the same both in the function declaration and in the function definition.

The names of formal arguments may be specified in both the function declaration and the function definition in C++, even if the compiler ignores those in the declaration. Providing names for function arguments is a part of the function documentation. The name of an argument clarifies how the argument is used and reduces the need to include comments in a function definition. It is also easier to refer to an argument in the documentation of a class if it has a name.

Return types

Always specify the return type of a function explicitly. Modern C++ compilers expect this anyway.
A public function must never return a reference or a pointer to a local variable.

If a function returns a reference or a pointer to a local variable, the memory to which it refers will already have been deallocated when this reference or pointer is used. The compiler may or may not give a warning for this.

Inline functions

Do not use the preprocessor directive #define to obtain more efficient code; instead, use inline functions.
Use inline functions when they are really needed.

Inline functions have the advantage of often being faster to execute than ordinary functions. The disadvantage in their use is that the implementation becomes more exposed, since the definition of an inline function must be placed in an include file for the class, while the definition of an ordinary function may be placed in its own separate file. A result of this is that a change in the implementation of an inline function can require comprehensive re-compiling when the include file is changed.

The compiler is not compelled to actually make a function inline. The decision criteria for this differ from one compiler to another.

Temporary objects

Minimize the number of temporary objects that are created as return values from functions or as arguments to functions.

Temporary objects are often created when objects are returned from functions or when objects are given as arguments to functions. In either case, a constructor for the object is first invoked; later, a destructor is invoked. Large temporary objects make for inefficient code. In some cases, errors are introduced when temporary objects are created. It is important to keep this in mind when writing code. It is especially inappropriate to have pointers to temporary objects, since the lifetime of a temporary object is undefined.

Function length

Avoid long and complex functions.

Long functions have several important disadvantages:

  1. If a function is too long, it can be difficult to comprehend. Generally, it can be said that a function should not be longer than 60 lines (or one page), since that is about how much can be comprehended at one time. This is also what can be displayed in an editor window without scrolling.
  2. If an error situation is discovered at the end of an extremely long function, it may be difficult for the function to clean up after itself and to "undo" as much as possible before reporting the error to the calling function. By always using short functions, such an error can be more exactly localized.
  3. Complex functions are difficult to test. If a function consists of 15 nested if statements, then there are 2**15 (or 32768) different branches to test in a single function.