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

  1. Include files:
    1. Include files in C++ always have the file name extension .hh.
    2. Include files which also have to compile with ANSI-C have the extension .h
    3. An include file should not contain more than one class definition.
    4. 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.
    5. Every include file must contain a mechanism that prevents multiple inclusions of the file. Example
    6. 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.
  2. Inline functions:
    1. Use inlining only if you are sure that you benefit from it.
    2. Inline functions should be kept in a separate file. Inline definition files always have the file name extension .icc. Example
    3. Constructors and destructors must not be inline.
    4. About inlining forwarding functions:
    5. Do not use the preprocessor directive #define to obtain more efficient code; instead, use inline functions. Example
    6. Never include other files in an .icc file.
  • Implementation files:
    1. Implementation files in C++ always have the file name extension .C.
    2. Divide up the definitions of member functions or functions into as many files as possible.
    3. Place machine-dependent code in a special file.
    4. All files must name a responsible for this code. Example
  • Comments: Example
    1. All comments are to be written in English.
    2. Write some descriptive comments before every function.
    3. Use // for comments.
    4. 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.
    5. The standardization of comments makes it possible to automatically generate documentation from source code (eg. by using doc++ or docify).
    6. 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:
    1. 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.
    2. The names of variables, constants, and functions are to begin with a lowercase letter.
    3. Data member are to begin with a lowercase letter.
    4. Accessor functions are to begin with an lowercase letter.
    5. 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.
    6. The names of classes, structures, typedefs, and enumerated types are to begin with an uppercase letter.
    7. 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
    8. Never use identifiers which begin with one or two underscores.
    9. A name that begins with an uppercase letter is to appear directly after its prefix. Example
    10. A name that begins with a lowercase letter is to be separated from its prefix using an underscore (`_').
    11. A name is to be separated from its suffix using an underscore (`_').
    12. If the last letter in a word is in uppercase, an underscore is to be used as a word separator. Example
  • Classes:
    1. Encapsulate global variables and constants, enumerated types, and typedefs in a class.
    2. 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
    3. Member functions which are defined within the class definition should fit on one line. (They are automatically inlined. Typical examples are accessor functions.)
    4. All polymorphic class definitions must define a virtual destructor. Example
  • Functions:
    1. Always provide the return type of a function explicitly. Example
    2. 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).
    3. Always write the left parenthesis directly after a function name. Example
    4. 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
    5. Minimize the number of temporary objects that are created as return values from functions or as arguments to functions. Example, Example
  • Flow Control Statements:
    1. 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.
    2. Braces can only be omitted if the block is empty or contains single statement which fits in the same line as the control primitive.
    3. The code following a case label must always be terminated by a break statement. Example
    4. A switch statement must always contain a default branch which handles unexpected cases. Example
    5. Use goto only to jump out of nested loops.
    6. Always use inclusive lower limits and exclusive upper limits. Example
  • Memory Allocation:
    1. A class which uses new to allocate instances managed by the class, must define a copy constructor. Example, Example
    2. A class which uses new to allocate instances managed by the class, must define an assignment operator. Example
    3. Always pair new with delete. Do not use malloc, realloc and free!.
    4. Always use delete [] array-name when deallocating arrays. Example
    5. Always assign ZERO to a pointer that points to deallocated memory.
    6. Do not allocate memory and expect that someone else will deallocate it later. Example
  • Operators:
    1. An assignment operator which performs a destructive action must be protected from performing this action on the object upon which it is operating.
    2. An assignment operator ought to return a const reference to the assigning object. Example
    3. When two operators are opposites (such as == and != ), it is appropriate to define both.
    4. The dereference operator `*' and the address-of operator `&' should be directly connected with the variable name in declarations and definitions. Example
    5. Do not use spaces around `.' or `->', nor between unary operators and operands.
    6. Use parentheses to clarify the order of evaluation for operators in expressions. Example, Example
  • Operator Overloading:
    1. When overloading functions, all variations should have the same semantics (be used for the same purpose). Example
    2. Use operator overloading sparingly and in a uniform manner.
  • Miscellaneous:
    1. Use the const qualifier whenever possible.
    2. Constants are to be defined using const or enum; never using #define. Example
    3. Avoid the use of numeric values in code; use symbolic values instead.
    4. Avoid global data if at all possible.
    5. Use the Standard Library whenever possible


    Last modified: Tuesday, 19-Feb-2002 09:44:17 CET

    Thorsten Glebe