Programming in C++, Rules and Recommendations
Introduction
The purpose of this document is to define one style of programming in C++.
The rules and recommendations presented here are not final, but should serve as
a basis for continued work with C++. This collection of rules should be
seen as a dynamic document; suggestions for improvements are encouraged.
This document is a very much abridged version of the original Ellemtel document. See here for the copyright note.
The body of the paper focuses on a concise presentation of the most relevant points. Compared
to the original the distinction between rules and recommendations has been
lifted. The goal is to define a set of guidelines which, if followed,
ensures a certain minimal standard for code-quality.
In case of CLUE, be aware
that code contributions can be rejected if the coding conventions are violated
in an unacceptable way.
People are encouraged to also read the original Ellemtel document or other
similar collections of guidelines/conventions, which ususally will provide
more detailed explanations why certain rules make sense.
For an explanation of the C++ terminology, have a look at the following link:
C++ terminology .
Literature: the books 'Effective C++ programming' and 'More effective C++
programming' by Scott Meyers are strongly recommended.
Coding Conventions
- Include files:
- Include files in C++ always have the file name extension .hh.
- Include files which also have to compile with ANSI-C have the extension .h
- An include file should not contain more than one class definition.
- An include file for a class should have a file name of the form
<class name>.extension.
Use uppercase and lowercase letters in the same way as in the source code.
- Every include file must contain a mechanism that prevents multiple
inclusions of the file. Example
- Include files which are only needed for the implementation and not for
the class definition should not appear in the header file of the class but
only in the implementation file.
- Inline functions:
- Use inlining only if you are sure that you benefit from it.
- Inline functions should be kept in a separate file. Inline
definition files always have the file name extension .icc.
Example
- Constructors and destructors must not be inline.
- About inlining forwarding functions:
- Do not use the preprocessor directive #define to obtain more
efficient code; instead, use inline functions.
Example
- Never include other files in an .icc file.
Implementation files:
- Implementation files in C++ always have the file name extension .C.
- Divide up the definitions of member functions or functions into as many files as possible.
- Place machine-dependent code in a special file.
- All files must name a responsible for this code. Example
Comments: Example
- All comments are to be written in English.
- Write some descriptive comments before every function.
- Use // for comments.
- Note that comments in include files are meant for the users of classes,
while comments in implementation files are meant for those who maintain the classes.
- The standardization of comments makes it possible to automatically
generate documentation from source code (eg. by using doc++ or
docify).
- If the characters // are consistently used for writing comments, then the
combination /* */ may be used to make comments out of entire sections of code
during the development and debugging phases. C++, however, does not allow
comments to be nested using /* */.
Naming Conventions:
- The identifier of every globally visible class, enumeration type, type
definition, function, constant, and variable in a class library is to begin
with a prefix that is unique for the library, via the use of namespaces. This
enables you to use the same generic names within different context.
- The names of variables, constants, and functions are to begin with a lowercase letter.
- Data member are to begin with a lowercase letter.
- Accessor functions are to begin with an lowercase letter.
- A private data element which will be visible in a Root file and has the
same name as the accessor function are to begin with an uppercase letter.
- The names of classes, structures, typedefs, and enumerated types are to
begin with an uppercase letter.
- In names which consist of more than one word, the words are written
together and each word that follows the first is begun with an uppercase
letter. Example
- Never use identifiers which begin with one or two underscores.
- A name that begins with an uppercase letter is to appear directly after
its prefix. Example
- A name that begins with a lowercase letter is to be separated from its
prefix using an underscore (`_').
- A name is to be separated from its suffix using an underscore (`_').
- If the last letter in a word is in uppercase, an underscore is to be
used as a word separator. Example
Classes:
- Encapsulate global variables and constants, enumerated types, and typedefs in a class.
- The public, protected, and private sections of a class are to be declared
in that order (the public section is declared before the protected section
which is declared before the private section).
Example
- Member functions which are defined within the class definition should fit on one
line. (They are automatically inlined. Typical examples are accessor functions.)
- All polymorphic class definitions must define a virtual destructor.
Example
Functions:
- Always provide the return type of a function explicitly.
Example
- When declaring functions, the leading parenthesis and the first argument
(if any) are to be written on the same line as the function name. If space
permits, other arguments and the closing parenthesis may also be written on the
same line as the function name. Otherwise, each additional argument is to be
written on a separate line (with the closing parenthesis directly after the
last argument).
- Always write the left parenthesis directly after a function name.
Example
- The names of formal arguments to functions are to be specified and
are to be the same both in the function declaration and in the function
definition. Example
- Minimize the number of temporary objects that are created as return
values from functions or as arguments to functions.
Example,
Example
Flow Control Statements:
- Flow control primitives if, else, while, for and do usually should be
followed by a block, delimited by braces ({}) where the opening brace
is in the same line as the flow control primitive and the closing brace in
the same column as the first character of the control primitive.
- Braces can only be omitted if the block is empty or contains single statement
which fits in the same line as the control primitive.
- The code following a case label must always be terminated by a
break statement.
Example
- A switch statement must always contain a
default branch which handles unexpected cases.
Example
- Use goto only to jump out of nested loops.
- Always use inclusive lower limits and exclusive upper limits.
Example
Memory Allocation:
- A class which uses new to allocate instances managed by the class,
must define a copy constructor.
Example,
Example
- A class which uses new to allocate instances managed by the class,
must define an assignment operator.
Example
- Always pair new with
delete.
Do not use malloc, realloc and free!.
- Always use delete [] array-name when deallocating arrays.
Example
- Always assign ZERO to a pointer that points to deallocated memory.
- Do not allocate memory and expect that someone else will deallocate
it later.
Example
Operators:
- An assignment operator which performs a destructive action must be
protected from performing this action on the object upon which it is
operating.
- An assignment operator ought to return a const reference to the
assigning object. Example
- When two operators are opposites (such as == and != ), it is
appropriate to define both.
- The dereference operator `*' and the address-of operator
`&' should be directly connected with the variable name in declarations and
definitions. Example
- Do not use spaces around `.' or `->', nor between unary operators
and operands.
- Use parentheses to clarify the order of evaluation for operators in
expressions.
Example,
Example
Operator Overloading:
- When overloading functions, all variations should have the same
semantics (be used for the same purpose).
Example
- Use operator overloading sparingly and in a uniform manner.
Miscellaneous:
- Use the const qualifier whenever possible.
- Constants are to be defined using const
or enum; never using #define.
Example
- Avoid the use of numeric values in code; use symbolic values instead.
- Avoid global data if at all possible.
- Use the Standard Library whenever possible
Last modified: Tuesday, 19-Feb-2002 09:44:17 CET
Thorsten Glebe