C++ Style Guide

3BSE000831/B 1998-01-22 Approved

Summary

This guideline gives recommendation for C++ programming within ABB Industrial Automation and Drives.
The document was prepared by the IAD C++ Style Working Group consisting of people from the IAD centres in Germany, Sweden and Switzerland in 1991. The first revision was done 1996 to cover the experience done during 6 years of usage and describe new features added to the C++ language. This is the second revision mainly to adopt the standard to accept programs written for Microsoft's COM architecture and convert it to a guideline.

Contents

Detailed Table of Contents
1. INTRODUCTION
2. TERMINOLOGY
3. LANGUAGE ISSUES
4. FORMATTING ISSUES
5. OBJECT ORIENTATION ISSUES
6. SOURCE CODE REVIEW
7. CHECK LIST

Contents

 
1. INTRODUCTION
2. TERMINOLOGY
3. LANGUAGE ISSUES
3.1 Modularization
3.1.1 Filename conventions
3.1.2 C++ include files
3.1.3 Standard include file
3.1.4 Call to C routines
3.1.5 ANSI C compatible include files
3.2 Declarations
3.2.1 Naming conventions
3.2.2 Name booking
3.2.3 Data declarations
3.2.3.1 Pointers
3.2.3.2 Arrays and strings
3.2.3.3 Floating-point numbers
3.2.3.4 Integers
3.2.3.5 Characters
3.2.3.6 Booleans and enumerations
3.2.4 Constants (non-modifiable objects)
3.2.5 Structs
3.2.6 Unions
3.2.7 Bit-fields
3.3 Classes
3.3.1 Access control
3.3.2 Normal (Canonical) Form for Class
3.3.3 Constructors
3.3.4 Destructors
3.3.5 Assignment vs. initialization
3.3.6 Implicit type conversion
3.3.7 Abstract classes
3.3.8 Object allocation
3.4 Functions
3.4.1 Argument passing
3.4.1.1 Input-only arguments
3.4.1.2 Input/Output arguments
3.4.1.3 Function return value
3.4.1.4 Summary
3.4.2 Overloading
3.4.2.1 Overloading of operators
3.4.3 Inline functions
3.4.4 Friend functions
3.5 Expressions and statements
3.5.1 General coding principles
3.5.2 Efficiency, global vs. local
3.5.3 Error handling
3.5.3.1 Run-time errors
3.5.3.2 Programming errors
3.5.3.3 Exception
3.5.4 Memory allocation
3.5.5 Boolean expressions
3.5.6 Type conversions
3.5.7 Loop constructs
3.5.8 Break and continue
3.5.9 Switch
3.5.10 Input / Output
3.6 Preprocessor directives
3.7 Portability
3.8 Comments
4. FORMATTING ISSUES
4.1 Upper/Lower case
4.2 Spaces
4.3 Blank lines
4.4 Indentation
4.5 Structured statements
4.6 Line breaks
4.7 Comments
4.8 Semicolons
4.9 Commas
5. OBJECT ORIENTATION ISSUES
5.1 Introduction
5.2 Use of inheritance
5.3 Interfaces
5.4 Use protected or private members?
5.5 Aggregation as alternative to inheritance
6. SOURCE CODE REVIEW
7. CHECK LIST
 
 

1. Introduction

This document gives recommendations of how to write programs in C++ for applications within ABB Process Automation. The main purpose of this style guide is to state rules and hints in order to
It also explains things that usually are hard to understand for programmers starting with C++, and highlights the most important features.
This style guide is not intended as a primer for the language; on the contrary, the reader should already be familiar with C++.
The following books are recommended:
Bjarne Stroustrup:
The C++ Programming Language 3rd edition
Addison-Wesley Publishing Company, 1997
ISBN 0-201-88954-4

Bjarne Stroustrup, Andrew Koenig:
The Annotated C++ Language Standard
Addison-Wesley Publishing Company, 1998

James O. Coplien:
Advanced C++ Programming
Styles And Idioms
Addison-Wesley Publishing Company
ISBN 0-201-54855-0

Scott Meyers:
Effective C++
Addison-Wesley Publishing Company
ISBN 0-201-56364-9

Scott Meyers:
More Effective C++
Addison-Wesley Publishing Company
ISBN 0-201-63371-X

Erich Gamma et al.:
Design Patterns
Addison-Wesley Publishing Company
ISBN 0-201-63361-2

Steve McConnel
Code Complete
Microsoft Press
ISBN 1-55615-484-4

A summary of the most important style rules can be found in See CHECK LIST .

Most style rules are recommendations, not absolute rules. The code review group has the final responsibility for deviations from these rules, see See SOURCE CODE REVIEW .

Always add a comment when you break the rules, so that the reader of the code is aware of the fact that you broke the rules on purpose and why you did it .

We appreciate comments on this document, and ideas of new things that should be mentioned here. The intention is that this style guide should be a living document, which is continuously updated as our experience with C++ grows.

2. Terminology

Abstract class is a class that can be used only as a base class of some other class. A class is abstract if it has at least one pure virtual function.
Argument is actual argument in function call.
Assignment means that the previous value of an object is replaced. This is different from initialization, which occurs when an object is created.
Automatic data objects are local to a block, allocated on the stack, and are discarded on exit from the block.
Base class is a class from which other classes inherits.
Built-in type means a predefined datatype (e.g. int), in contrast with user-defined type.
Class object means simply an object of class type (user-defined type), as opposed to built-in type. It has nothing to do with meta classes found in other languages.
Constant usually means a literal value, like 1234 or `a'. This is not the same as an object marked const, which only means that the object cannot be modified after initialization.
Constant expression is an expression that evaluate to an integral constant, and only involves literals, enumerators, const values of integral types initialized with constant expressions, and sizeof expressions.
Declaration introduces one or more names (identifiers) into a program, and specifies how those names are to be interpreted.

Definition specifies the complete information for a declaration and is only allowed once.

Enumeration is a distinct integral type with named constants. The named constants in the enumeration type are called enumerators.

Free store is what we call the heap, i.e. the area where dynamic objects are allocated (as opposed to the stack where storage space for functions being executed is allocated).

GUID is a Globally Unique Identifier, a 128 bit long integer unique in space and time used to identify a COM interface.

Header file is the same as include file.

Initialization is the process of giving an object an initial value at the time it is created. This is not the same as assignment, although the sign "=" can be used in the object definition.

Integral type means the types char, int of all sizes, and enumerations.

Integral promotion is the one sort of standard type conversion where a char, a short int, enumerator, object of enumeration type, or an int bit-field (in both their signed and unsigned varieties) may be used wherever an integer may be used. Integral promotions are value-preserving, that is the interpretation of the value will not change.

Lvalue is an expression referring to a named region of storage, i.e. something that can be used on the lefthand side of an assignment, with the exception of const objects which are Lvalues but cannot be assigned.

Member function is a function defined inside a class. We should not call it a method as in other languages. In language-independent discussions, the term method is acceptable, although service often is better.

Member data means data objects declared inside a class. We should not call it an instance variable as in other languages. In language-independent discussions, use the term attribute.

Object is a region of storage. It can be of any type. If it is of class type, we call it a class object.

Parameter is formal parameter in function declaration.

Pure virtual function is a member function without definition in an abstract class. It can also be called abstract function.

Static can mean a lot of things depending on the situation. Inside a function, static data objects retain their values across exit from and reentry to functions and blocks. Marking a function or data object static on the outermost level of a file means that it has internal linkage, i.e. is local to that file.

Static member is a class member that is shared by all instantiated objects of this class. We should not call this a class variable.

Storage class defines how a data object is allocated. This can be static or automatic.

User-defined type is the same as a class type (including structures and unions)

Virtual means different things depending on the situation:

Virtual base class - A base class whose representation is shared with every other occurrence of that base class in an object, although the base class occurs several times in the inheritance graph.

Virtual function - A function which allow derived classes to provide alternate versions for a base class function.

3. Language Issues

Contents

(see also detailed contents)
3.1 Modularization
3.2 Declarations
3.3 Classes
3.4 Functions
3.5 Expressions and statements
3.6 Preprocessor directives
3.7 Portability
3.8 Comments
 
Some programming languages try to make it harder to write bad programs. Others try to make it easier to write good programs. C++ is one of the second kind.
C++ gives you good possibilities to write good programs, but it does not stop you from writing unreadable programs that are impossible to maintain. This is mainly due to its compatibility with the C language. This chapter will give some information to help you write good programs in C++.

3.1 Modularization

Contents

3.1.1 Filename conventions
3.1.2 C++ include files
3.1.3 Standard include file
3.1.4 Call to C routines
3.1.5 ANSI C compatible include files
 
There are two ways of modularization and encapsulation in C++, a file or a class.
Generally, a module should contain things that are logically related. Modules have an interface part that defines how to access the module, and an implementation part that encapsulates the internal representation.
Dependencies between modules should be minimized. A good modularization is characterized by locality, i.e. the knowledge of the representation is local. This facilitates maintenance and understanding of the system.
A set of functions can be collected into a module by placing them in the same file . Functions and data can be made internal to the module by use of an anonymous name space or declaring them static . The interface to the module is declared in an include file.
The preferred way of modularization, however, is to use classes and object-oriented programming techniques, especially if the module maintains internal data structures.
A class is split into two parts, its declaration and its implementation. Normally, each of these parts should be a separate file. The declaration (including private data members and inline functions) is placed in the include file and the implementation in the code file.
Do not use global variables (or public variables in classes). Encapsulate the data inside a class instead, and provide member functions for access to the data.
If a set of nonmember functions work intimately together with a special class, they may be declared in the same include file as that class.
Similarly, classes that are designed together, and also intended to be used together, may be declared in the same include file. In some cases, this can give the user a better overview of the relations between the classes. Normally, however, use a separate include file for each class.

3.1.1 Filename conventions

The following file types (file extensions) shall be used:
 
.H
C++ include file
.h C/C++ include file
.CPP/cpp C++ implementation file for Windows or VxWorks
.CXX/cxx C++ implementation file for Windows NT and Unix platform
.C  C++ implemenation file for Unix only
 
The .H extension means that the include file is only possible to use from C++, whereas .h indicates that the include file is possible to use both in C++ and in ANSI C. See also See ANSI C compatible include files .
The include file for the class MyClass should be MyClass.H and the corresponding implementation file should be MyClass.C . Filenames should always be in the same case as the class name.
A file without a class should have a descriptive name summarizing the meaning of the file.

3.1.2 C++ include files

To use a class, the application should only need to include the include file for this specific class. If other classes are needed by the class declaration, these include files should be included from within the include file. Nested include files are common in C++ and should be encouraged. Be careful, however, not to include files in the interface part, that really are needed only in the implementation part.
A method to avoid nested include files, is to use a pointer to the dependent class, and only forward declare a class within the include file. This method significantly reduces recompilation time when an include file has to be changed.
An include file must never contain function definitions (except inline functions) or data definitions (except simple constants)
To avoid that include files are included twice, each include file must be conditionalized using preprocessor directives. The include file for MyClass should be structured as follows:
The filename in the #include directive should be surrounded by double quotes, except for standard include files which are referenced using angle bracket notation.
For COM C++ header files may the header file be conditionalized using a GUID identifier.

3.1.3 Standard include file

A standard include file is provided at
/ipa/products/c++_complib/ version /include/gcstandard.H
Please consult your PC-Admin for location when installed locally in a Windows NT environment.
It shall be used by all C++ applications developed at ABB Industrial Systems.
It currently contains:
gcInt16 gcInt32 gcInt64 gcUnsigned16 gcUnsigned32 gcUnsigned64
The above integer types should only be used when knowledge about the exact size is absolutely required. See also Integers , and Bit-fields .

3.1.4 Call to C routines

It is possible to call C libraries from C++. The indicator extern "C" is required to be able to link a C++ program with external C routines. It is also used for C++ functions that are to be called from C, e.g. callback routines. It may also be needed for globally accessable data structures. Use the indicator extern in header files, avoid using it in implementation files.
In some cases, it may be good to change the original interface to a more object-oriented interface suitable for C++. Give this a thought before you make a translation of a .h file. Encapsulations of calls to the operating system also facilitate porting to other environments.

3.1.5 ANSI C compatible include files

Include files intended to be used in both C and C++ can be conditionalized using the following predefined preprocessor symbols:
__cplusplus Defined if the compiler is C++
__STDC__ Defined if C compiler, and follows ANSI C

If ANSI C compatibility is required, also note the following:

For compiler specific preprocessor symbols, see the compiler handbook, or the platform specific style guide if available.

3.2 Declarations

Contents

3.2.1 Naming conventions
3.2.2 Name booking
3.2.3 Data declarations
3.2.3.1 Pointers
3.2.3.2 Arrays and strings
3.2.3.3 Floating-point numbers
3.2.3.4 Integers
3.2.3.5 Characters
3.2.3.6 Booleans and enumerations
3.2.4 Constants (non-modifiable objects)
3.2.5 Structs
3.2.6 Unions
3.2.7 Bit-fields
 

3.2.1 Naming conventions

Names (identifiers) shall always be descriptive, i.e., they shall describe the purpose of the things they denote. All names shall use the English language. Use long names because it makes the code easier to read and understand. Names must be pronounceable.
Names should contain only alphanumeric characters (a-z, A-Z, 0-9) and underscore (_).
Single-word names should be given in full length, without abbreviations. If the name is constructed from several English words, an abbreviation may be used as part of the name, but only if the meaning is clearly understandable in the context of its use. Abbreviate a name only if it saves more than three characters.
Consistency and uniformity are very important. Try to use the same words everywhere you refer to a concept, both in identifiers, comments and design documents. It can often be good to make a dictionary of the words that are common in the application domain. Good use of names makes it considerably easier to understand programs and documentation.
When maintaining old code, use the same naming style as the original code uses. Also if you extend the C++ standard library, use the C++ standard library naming style.
Types (classes, structs, enums, typedefs) should be named with noun phrases .
Variables should be named as qualified type names , e.g. adjective + type name.
Counter variables can use the abbreviation NoOf meaning number-of.
Constants often describe a limit within a program. In these cases it is appropriate to use the prefix "max" in connection with the type name. Otherwise treat the names for constants like variable names.
Functions should be named with verb phrases , e.g. verb + type name.
For COM-interfaces these special rules should be applied:
 
Boolean variables and functions should be named with phrases of the form "to be" . Negated forms (isNot...) should be avoided.
When you declare objects of class type, try to give them names so that the total appearance "object.function" makes sense and is readable. The class designer should also consider this when the names of member functions are selected.
Names that consist of several words should be constructed in a way that makes the division clearly visible. This is done by capitalizing the first letter of each word (do not use underscore for this purpose).
We recommend that names of class data members start with "m_".
The first character (after a possible prefix) in the name is treated differently. It should indicate what the name stands for, in the following way:
Exception: COM interface classes starts with uppercase I and implementation classes starts with an uppercase C before the prefix.
Type names start with a capital letter.
Compile-time constants start with a capital letter. To emphasize that it is a constant, it may be written in all capital letters. But this should only be used for global constants with short names that cannot be divided into words.
Enumeration names start with a capital letter (they are also compile-time constants).
All other objects start with a lower case letter.
Function names start with a upper case letter.

Examples:
 

3.2.2 Name booking

Global names shall be booked using a name booking service. Unique 2-3 character prefixes shall be booked at ISY/QA for the software product components, to avoid booking of every single name. The prefix may be extended (locally administerred) to up to 5 characters, optionally followed by an underscore. The prefix may have to be an abbreviation, but it should still have a meaning and not be a random letter combination.
Global names are
Declarations inside a class, e.g. member functions, need not be unique since they are in local scope.
Names of static functions and data need not be booked either, since they are not visible outside the file. All functions and data at the outermost level that are not required to be externally accessed, must be declared static, or use an anonymous name space.
Be careful not to collide with names defined by ANSI C, X Windows, POSIX, or other commonly used C libraries.
C++ identifiers are case sensitive. To avoid confusion, however, do not declare identifiers that are different only in character case. In the same way, the reserved C++ keywords should be considered reserved also in uppercase and mixed-case.

3.2.3 Data declarations

Each data object should be declared on a separate line. This is absolutely required if the declaration is a pointer because the asterisk is only associated with the first name:
A constant must be initialized at the point of declaration. For variables, the initialization is optional, but strongly recommended. There are very few good reasons for introducing a local variable without initializing it. Note that a declaration can occur anywhere a statement can; don't declare a variable before its initial value is known.

Never declare a variable together with the type declaration of a class, struct, union, or enum. All these types should be given an explicit name, which is used in variable declarations later on.

Don't use the same variable for different purposes. Declare a new variable for each purpose, and define it close to the place where it is used. This is particularly important for temporary variables and loop indices.

Note that blocks can be nested in C++. This can be useful to limit the scope of a variable.

Use locally declared loop variables where possible. Never use the loop variable outside the loop.

3.2.3.1 Pointers
In pointer declarations, the pointer indicator * is placed immediately after the type identifier, that is, the asterisk is associated with the type, not the variable. Example:
This is not the practise in the original C style, but for simple pointer declarations it is the layout that is most readable in C++. More complex declarations, for example with more than one asterisk, often indicates a bad data structure design. Try to encapsulate the data structure in a class instead.
The only exception is pointers to functions, which are quite useful. They must be declared with extra parentheses:
This can be made more readable using a typedef in two possible ways:
or
Note that a function name is always evaluated to the address of the function. Therefore, the address-of operator & need not be used in front of sqrt above. Likewise, when the function is called via a pointer, it need not be dereferenced using the * operator. Use the simple call
instead of explicit pointer notation
Common uses of pointers to functions are
The special case of pointers to member functions is discussed in the C++ reference manual.
Portability to a 64-bit architecture means
3.2.3.2 Arrays and strings
The predefined array type in C++ ( [] ) is badly designed and should not be used. It should only be used for low-level programming where performance is critical, and in calls to C library routines when their interfaces require it. The predefined array type has many drawbacks:
Therefore, array objects should not be global or sent as argument values. Encapsulate arrays in a class, or use a generic Array or List class from the C++ Standard Library instead.
All strings that may need translation should be separated from the C++ source.
3.2.3.3 Floating-point numbers
Three predefined floating-point types are provided: float , double , and long double . The floating-point types cannot be declared unsigned.
3.2.3.4 Integers
The integer type int is in many cases the default in C++, and need not be mentioned explicitly. This may only be used in the following cases, where the type modifier can be regarded as a type by its own:
 
Allowed abbreviation
Actual type
short 
short int
long  long int
unsigned  unsigned int
 
In all other cases, int must be specified explicitly. For example, always write
instead of
Unsigned number should be used in all cases where negative numbers are not applicable, e.g. for counters, and size and length variables.
To be portable to 64-bit architecture, never use 0xFFFFFFFF to specify a value of -1. This is just a large integer if the type is long.

In addition to the predefined types, the standard include file gcstandard.H defines integers with known size, e.g. gcInt16. But these types should only be used if the exact size really is important. The normal integer types are int and unsigned . They can always be assumed to be at least 16 bit. If larger numbers are required, use long or unsigned long .

The type char (without " signed "/" unsigned ") must never be used for numerical types. For integer sizes smaller than 16 bit, use bit-fields .

3.2.3.5 Characters
Use the types char or wchar_t for characters, never unsigned char or signed char. The C++ Standard Library classes string and wstring are preferred instead of char* and wchar_t*.
If a Windows program should be able to compile both for 8-bit character strings and Unicode character strings, you can instead use TCHAR and basic_string<TCHAR>.
Never use type char (without " signed" or " unsigned" ) to represent numbers that have nothing to do with characters.
3.2.3.6 Booleans and enumerations
C++ has a predefined Boolean type bool ( true or false ), but until all compilers support it, we provide a definition for it in our standard include file
gcstandard.H.

Never use integer codes when you really mean true or false .

Input arguments to functions should preferably not be of boolean type. The reason is that it is hard to see what a function call like

really means.
It is better to define a special enumeration type with meaningful names for the alternatives. Note that enumerations follows scope rules, so they can preferably be declared inside a class.

Note also that it is not a good idea to have functions that do fundamentally different things depending on the value of an input parameter.

One good use of the Boolean type is to use it for function result values (such a function should be named with a phrase of the form "to be"). Example:

If you need to convert from a boolean represented by an int to a built-in bool, use the method shown below:

3.2.4 Constants (non-modifiable objects)

An object of any type can be specified to have a constant value throughout the scope of its name, by the specifier const . A const object must be initialized at the same time it is created. If it is declared in local scope, it will be reinitialized every time this scope is reentered.
A const object may have a value that is not known until the instance of the object is created. The name const is rather misleading; it does not define a constant in its normal meaning. It only means that the object cannot be modified using assignment. Example:
Every object whose value is initialized only and doesn't change thereafter, should be explicitly declared as const.

Explicitly declare dependencies between constants.

Example:

const should be used wherever it is possible to use it.
The keyword const explicitly states that the programmers intention is that the object should not change. It makes it easier to read and maintain a program, and it also makes it easier for the compiler to check the consistency of the program.

All constants must be defined using the keyword const . Never use the preprocessor directive #define to define constants!

The syntax for the different uses of const is not obvious, so it is described here using some examples:

Non-modifiable objects:

Variable pointer to non-modifiable object:
Non-modifiable pointer to variable:
Non-modifiable pointer to non-modifiable object:
Non-modifiable formal arguments:
Non-modifiable object as function result:
Non-modifiable objects of class type:
Constant member functions (cannot modify member data):
Constant member data with same value for each instantiation:

 

Constant member data with different value for each instantiation:

 

3.2.5 Structs

A struct and a class are identical in C++, except for the access control (private, public, etc.). The following rule shall be used, however:
If a struct is intended to be used in ANSI C applications also, it should be declared in the following way (in an include file .h common for both ANSI C and C++):
This is identical to the normal C++ declaration

3.2.6 Unions

Use of unions is not recommended. The primary use of unions is for type conversions, but this can be handled in better ways in C++.
If you want to declare a data structure that can store different things in different situation, try to use a class hierarchy and virtual functions instead.
Anonymous unions inside a class or a struct may be needed if space consumption is critical. Always use an explicit tag field in these cases, that defines which union alternative is the current one.

3.2.7 Bit-fields

The size of an integral member of a class, struct, or union can be specified using a bit-field.
You cannot specify more bits than are used to represent type int. With normal size of int , this means that bit sizes from 1 to 15 are allowed.
Use bit-fields when memory-consumption is critical. But note that the physical layout of bit-fields is highly implementation dependent. For example, some compiler implementations may align each member on a word boundary. Also be aware of that the allocation order may be computer architectures dependent.

3.3 Classes

Contents

3.3.1 Access control
3.3.2 Normal (Canonical) Form for Class
3.3.3 Constructors
3.3.4 Destructors
3.3.5 Assignment vs. initialization
3.3.6 Implicit type conversion
3.3.7 Abstract classes
3.3.8 Object allocation
 
A class is a user-defined type. A class declaration specifies the representation of objects of the class and the set of operations than can be applied to such objects.
A class is also a scoping mechanism (like namespaces which are not described in this version of this document). Names declared inside a class are local to that class. To access these names outside the class declaration, the names must be qualified by the name of the enclosing class: ClassName::LocalName .

3.3.1 Access control

The different class sections should be specified in the following order (private must be given explicitly):
Data members should be private. If derived classes need access to these data members, make a protected interface with access functions to them. If the data format later change, only the access functions need to be changed. The derived classes, located in other source files, are not affected. The access functions can be declared inline to improve performance.

An access function can be used both to retrieve and store a value, if a reference is returned:

In some cases protected access may have to be used directly to data, but be aware of the consequences: the derived classes will be a strongly coupled with the base class. The general rule is that it should be possible to design derived classes, without having access to the source code of the base class implementation. Therefore, the base class must be substantially better documented when it allows protected access to data. See also OBJECT ORIENTATION ISSUES .
Friend functions and friend classes can be also used to break the abstraction barrier, and should also be used with care. See Friend functions .

When a subclass is declared, it can either inherit from the base class as private or public . Public means that an object of the derived class can be used everywhere the base class can (subtype relation). If this is not the case, the inheritance must be made private.

A typical case of private inheritance is if a stack inherits from an array. We do not want to allow that a stack is input to a function that expects an array, since it may modify the object in a way that is inconsistent with the semantics of a stack.

3.3.2 Normal (Canonical) Form for Class

Classes should have the following form:
A default constructor (a constructor without arguments), the copy constructor, the destructor, and the assignment operator are automatically generated by the compiler if you leave them out. H owever, we strongly recommend that you define them explicitly; this is of ultimate importance for classes that contain pointers.
If assignment and initialization are not applicable, disallow it by redefining these routines in the private section.

The implementation of a the class like above, assuming data members
memberA, memberB and a base class MyBase , typically looks like:
 

The data members are initialized by the compiler in the order they have been declared in the header file, and not necessarily in the order they are mentioned in the initialization list of the constructor. We strongly recommend that you use the same order in the initialization list as in the header file. See the following example, which does not do what you might think:

 

3.3.3 Constructors

Each class must have constructors that set the object in a well-defined state. Call to a public member function Init() should be avoided (although a private Init() function can be useful if several constructors have much in common).
However, an Init() method is the only method to initialize when using COM, or when creating an array of objects with one call.
A constructor with exactly one parameter should be used with care. It has two meanings: Besides defining one way to initialize the object, it also specifies an automatic type conversion that the compiler can attempt whenever appropriate, and without warning. This is described more in Implicit type conversion .
Data objects in the class are initialized using the member initialization list, also if they are not of class type. Wherever possible, use initialization instead of assignment. For example, write
instead of
And make m_internalInt const if it never will change after the class object has been initialized. But if the constant is the same for all instantiations, make it a static const member instead.
The member initialization list is also used to pass arguments to a base class constructor from a constructor in a derived class.

3.3.4 Destructors

A destructor shall always be defined if the class contains members that are pointers or allocates any system resource.
Declare the destructor virtual in every base class that has virtual functions. Otherwise, objects of the derived classes cannot be deallocated.
A virtual destructor should not be inline.

Note that automatic deallocation via the destructor does not occur when setjmp/longjmp are used.

3.3.5 Assignment vs. initialization

It is important to know the difference between assignment and initialization, and how this is implemented using implicitly defined constructors and operators.
An assignment operation always replaces the previous contents of an object. For example, a class containing pointers usually needs to free the memory of the old value when a new value is assigned. The only way an object could not have a previous contents is if it is being given a value at the same time it is first created; such an operation is initialization, not assignment.
An assignment occurs only as a result of executing an assignment operator (operator=) as part of an expression. Initialisation is always expressed as constructors.
Example:
The use of the copy constructor is not obvious. It is used in the following cases:
As an example, assume the function definition
In a function call
the copy constructor will be used to initialize the formal argument arg , and it will also be used in the return statement to initialize a temporary object, which will then be assigned to upper using operator=.
Note that initialization of members must always be done in the order in which the members are declared, otherwise dependencies between the members may cause errors.

3.3.6 Implicit type conversion

It is possible to specify that the compiler should do implicit type conversions for user-defined types (classes). This is common in C++ for built-in types, and C++ allows you to implement the same behaviour for user-defined types.
But if an implicit conversion is not obviously necessary, leave it out. Programs with a lot of implicit type conversions are hard to read and understand. Implicit type conversion can be useful for basic user-defined types that can be regarded as predefined in the language, but use it carefully and with restraint.
A definition of type conversion to a class type is specified as a constructor with one argument, and type conversion from a class type is specified as an operator:
To avoid that constructors taking a single argument become conversion operators, use the keyword " explicit ". Some compilers do not yet support this keyword; you can use #define explicit with those compilers. Even with those compilers, it is good to have the keyword in the code so that the maintainer/user of the code sees what is intended.

FromType and ToType can either be other class types or built-in types like int.

Use type operators only to achieve a significant notational convenience. Normally, it is better to declare explicit conversion functions:
 

3.3.7 Abstract classes

A class which is not intended to be directly instantiated to objects, but only used as base class for derived classes, must explicitly define the not-yet-implemented virtual functions as zero:
This way the compiler complains if you try to instantiate the class directly.
This kind of class should begin with a clear comment
that indicates that the class cannot be instantiated directly. This is useful because it can be hard to see the "= 0" somewhere down in the declaration.

3.3.8 Object allocation

The programmer must often make the choice whether to allocate an object directly on the stack (automatic allocation) or via a pointer on the free store.
Automatic allocation can be made in three different notations (here initialization represents the arguments to the constructor, or another object of the same class):
Never return an automatic allocated object from a function. Observe that a call to a function with a static non-const string may generate an automatic allocated variable.

 
 

Free store allocation has only one notation:

Class objects inside a class (preferably in the private section), should be defined as pointers. The declaration of these internal classes need not be mentioned in the include file. This reduces the dependencies between modules, and the compilation time reduces significantly.

 

The deallocation of an internal class object can easily be handled by declaring a destructor.

Class objects allocated inside a function can be allocated either on the stack or on the free store, depending on the situation. Stack allocation is often better:

But:
Global class objects can be used to implement initialization and cleanup in a program. The constructors of such objects are called before the function main() is called, and the destructors are called when the program ends.

3.4 Functions

Contents

3.4.1 Argument passing
3.4.1.1 Input-only arguments
3.4.1.2 Input/Output arguments
3.4.1.3 Function return value
3.4.1.4 Summary
3.4.2 Overloading
3.4.2.1 Overloading of operators
3.4.3 Inline functions
3.4.4 Friend functions
 
A function in C++ can either be a separate function at the outermost level of a file, or a member of a class. A separate function is used as a normal function in procedural languages like C. A member function is accessed via an object using the following notation:
Provided -> is not overloaded, the last notation is just a shorthand when the object is allocated via a pointer, to avoid writing
Both kinds of functions can be declared as friends to one or more classes. This is only an implementation detail, and does not affect how the functions are used. Friendship is not specified together with the functions themselves, but instead in the declarations of the classes that grant them access. See See Friend functions for more details.
Finally, functions may have operator names, in which case they can be called using operator notation. For example, an overloaded definition for the operator + can be called either as
or as

3.4.1 Argument passing

When a function is called, each formal argument is initialized with its actual argument. Standard and user-defined conversions are performed.
C++ does not require that argument names are used in the formal declaration in the include file. However, to make the interface more readable, use argument names in the function declarations.
Do not use global variables instead of argument passing.
If you have more than 4 arguments to a function, check your design again!
3.4.1.1 Input-only arguments
The default mechanism for passing arguments to a function is call-by-value, i.e., all the actual argument values are copied. This is the desired way of passing simple arguments like integers and enumeration types. Example:
It can, however, be noticeably more efficient to pass a large object by reference than to pass it by value. In this case, the argument must be declared const to indicate that the reference is used for efficiency reasons only and not to enable the called function to change the value of the object.
Alternatively, the call-by-reference semantics can be implemented by using explicit pointers. The argument must also here be declared const to indicate that the argument is not changed by the function. An explicit pointer argument can be useful if it is likely that the caller of the function already has the object allocated via a pointer. See the discussion in Object allocation .
If the argument is a reference, the caller is required to provide a reference to an object, but if the argument is a pointer, it is possible for the caller to give it the value NULL.
Examples:
The caller of a function can normally assume that the objects given as actual arguments only are used by the called function during the call, and that the caller may deallocate them after the call. Special functions that internally save addresses of their arguments (i.e. to objects in the scope of the caller) can be needed in some situations, but they must clearly specify this in the comment describing the function.

Default arguments

An input argument that usually have a specific value can have this specified as a default initialization. Default arguments will be used in function calls where trailing arguments are missing. Like normal object initialization, the default values can be expressions that are calculated at run-time. Example:
 

Avoid the use of defaulted arguments, if the choice of default value is not obvious. Defaulted arguments should mainly be used to specify a simple interface that in special situations can be customized by changing the given default values.

Note that default arguments can only be declared once; this is normally done in the function declaration in the include file, and not in the function definition.

In the actual function call, the defaulted arguments can only be dropped from the end. It is not possible to use ",," to mark that the default value should be used in the middle of the argument list. Therefore, before defining defaulted arguments, think of how the function is likely to be called, and arrange the arguments in a suitable order.

3.4.1.2 Input/Output arguments
It is preferable if functions do not have side-effects, and only return values using their function results, except when COM-interfaces are used. Then the return value is always the status (HRESULT) from the call.
A general drawback with functions that modifies its arguments is that the actual variables cannot be initialized, as recommended for all data objects ( See Data declarations ). They can only get values via assignment from the function. And there is not any support from the language to see the difference between pure output arguments, and input/output arguments where the input argument must be valid.
A function may only return one value. If the program's logic requires that multiple values be returned, this can be done in three ways:
A struct can be used if the data fields are logically related, for example X and Y coordinates for a point in a window.
A reference can be used to implement a function that is supposed to change the value of its argument (compare VAR in Pascal).
Explicit pointer arguments can be used as an alternative to reference arguments, but we generally recommend you to use references instead.
Note that C++ requires that for non-const references, the formal and actual argument have the same type. No standard conversions are attempted, as done for normal argument initialization.
3.4.1.3 Function return value
Consider a function defined as
The language defines that the function return is equivalent with an initialization of the form:
The function result, if not void, can be returned in three ways: Using copy, reference, or a pointer. In addition, the result can be marked const , which means that the result cannot be modified.
It is hard to recommend which methods should be used, but the decision must be made from a couple of things:
3.4.1.4 Summary
Summary of the most common argument passing mechanisms:
 
Input:
Output:
Function result:

3.4.2 Overloading

Function name overloading allows a set of functions that provide a common operation on different argument types to share a common name. This is a convenient shorthand to avoid inventing different names for functions that conceptually do the same thing. Example:
3.4.2.1 Overloading of operators
Operators can also be overloaded. In this case, we must take care of that the predefined meaning remains, and that the meaning is obvious. Do not overload operators in non-intuitive ways. An example of bad use of operator overloading is to define += to mean push on a stack.
A user-defined operator function must either be a non-static member function, or take at least one argument of a class or a reference to a class.
Overloaded definitions of operator= , operator[] , operator() , and operator-> must always be non-static member functions.
Operators that change their left operand, such as operator+= , should be defined as member functions.
Binary operators like operator+ should be defined as global functions to allow standard and user-defined operators to be applied uniformly to both operands. They can be defined as friend functions if access to the internal representation is needed. This is discussed in Friend functions .
Do not define only one of a set of operations, e.g. do not define operator== without defining operator!= .

3.4.3 Inline functions

Use inline as a significant optimization for very small functions. When used in the right situations, it can greatly increase the efficiency. However, inline is only a recommendation to the compiler, so consult your compiler guide for details about when inline has an effect.
But do not use inline functions excessively, especially not in class declarations, in which case the implementation must be placed in the include file. This can cause a lot of recompilations when the implementation changes.
Inline functions are also considerably more complicated to debug than normal functions.
Inline member functions should be defined after the class declaration, and not inside it. This makes the public class declaration more clean and easier to read.
Example:
Also virtual functions can be inline, although they will only be inlined when dynamic binding is not used, i.e. when the object is accessed directly and not via a pointer.

3.4.4 Friend functions

A friend to a class is a function that has the same access privileges as if it were a member of that class. Each class declaration nominates the specific functions that are to be its friends. As a notational convenience, a class can declare all member functions of another class as its friends.
The friend specifier is only an implementation detail. A function is called in the same way independent of whether it is a friend or not. A friend function belongs outside the class, either as a separate function or a member function of another class. The friend declaration in the class declaration is merely a pointer to the outside function definition.
Functions that on first sight should be declared as members, since they logically belongs to the class, should sometimes be declared as friend functions for the following reasons:
First, a member operator with one argument of the same class type has the drawback that it is asymmetric. Implicit type conversion is allowed on the right hand operand but not on the object, through which the member operator is called. For example, myString + `a' is possible but not `a' + myString . This is solved by declaring the operator as a separate function with two arguments. To give it the same access privileges as the original member, it can be declared as a friend.
Second, a member operator with one argument of another class type has the drawback that the order of the operands is fixed. The member operand must come first. The order can be changed by declaring it as a friend function instead. This is common when you want to use " cout << myObject " for your user-defined classes:
Third, friend functions or friend classes may have to be used for efficiency reasons when classes are designed to work closely together, for example a mathematical vector and matrix class. A similar case is when the private representation of a class is collected into an internal help class.

3.5 Expressions and statements

Contents

3.5.1 General coding principles
3.5.2 Efficiency, global vs. local
3.5.3 Error handling
3.5.3.1 Run-time errors
3.5.3.2 Programming errors
3.5.3.3 Exception
3.5.4 Memory allocation
3.5.5 Boolean expressions
3.5.6 Type conversions
3.5.7 Loop constructs
3.5.8 Break and continue
3.5.9 Switch
3.5.10 Input / Output
 

3.5.1 General coding principles

Programming is not just to get a working version of the program; in order to maintain and understand the program, the program must be well-structured, symmetric, and consistent. Data modelling, data abstraction, structured programming, modularization, etc. are methods that can be applied to make the inherent structure explicit.
These ideas can be applied also on the lowest level, when the function definitions are coded, as sketched below.
Try to avoid code duplication. One algorithm should only be coded in one place. If you need a variant of the algorithm, try to see if these two algorithms has common subparts, which can be made into common functions.
Do not duplicate a complicated expression or subexpression:

 
Bad 
Better 
a = arr1[start() + 3 * i];
b = arr2[start() + 3 * i];
const int index = start() + 3*i; 
a = arr1[index]; 
b = arr2[index]; 
 
This has nothing to do with efficiency; the compiler will probably make the same optimization anyway. The reason is that it makes the code easier to read, since it is easier to see that the same index is used (in reality, the expression is often more complicated than in this short example).
Do not duplicate a sequence of statements:
 
Bad 
Better 
if (ptr > Max) error(); 
stack[ptr++] = item1; 
. . . 
if (ptr > Max) error(); 
stack[ptr++] = item2;
void Push(...) {...} 
   . . . 
Push(item1); 
   . . . 
Push(item2); 
 
If efficiency is a problem, make the function inline.

3.5.2 Efficiency, global vs. local

Efficiency is important in our real-time applications and cannot be ignored during design of our C++ programs, but it is important to see the difference between global efficiency and local efficiency.
Global efficiency concerns the total design of an application. We must not design applications that are inherently inefficient. This regards the design of module interfaces. Algorithms that are known by several modules must be designed with great care. For example, avoid global search loops which concerns several modules.
Local efficiency concerns the implementation of individual modules. Don't think too much about the efficiency at this local level when you first design a module. For example, minimize the use of inline functions. Later on, when the complete application has been developed, performance measurement tools can be used to identify the efficiency bottlenecks. They can usually be easily removed, since they are localized inside the implementation parts of some modules.
In some cases it can be useful to think about local efficiency already from the start, and it is when basic datatypes like a string class is developed. These kind of modules can be regarded as predefined in the language, and they will be used extensively by most applications. The implementation of these kind of modules should already from the start be designed for maximum efficiency, since they surely will affect the efficiency of the whole system.

3.5.3 Error handling

Two types of errors can occur during program execution: Run-time error caused by events in the environment, and programming errors (bugs). They are normally handled in different ways.
3.5.3.1 Run-time errors
Run-time errors are common in our applications, and must be handled by our programs. Typical examples:
There is an exception mechanism in C++, but it should not be used for "ordinary" error handling. A function that detects a run-time error should return an error status, or call a supplied error handling function.
Whenever applicable, methods for engineering out failures are preferable to methods dealing with failures once they have occurred.
A common case is that we have a pure function, with only input arguments, to which error handling has to be added. In many cases, where timing problems cannot occur, the following solution can be used:
The class may contain a "cache", so that work done by the function dataIsOK need not be repeated when processData is called. Alternatively, the following method may be used:
A less elegant, but maybe more efficient solution, is to add a reference argument with an error status:
It is not recommended to rearrange the values as in
because the function result should always be the important part of the work done by a function. An exception is within a COM-interface where this type of error handling is the normal case.
Calls to the Unix operating system, though, normally follows that pattern:
ErrorValue is usually -1, but this can vary between different functions. Always check the return status after such system calls.
3.5.3.2 Programming errors
An interface to a function normally includes an assumption, or restriction, on input data. If these assumptions are not followed, it is regarded as a programming error made by the implementor of the calling function.
Note that assert must not be used to assume valid values from external sources, like users or hardware, nor is it in any way a replacement of correct error handling. Assertion should only be used to handle design errors where the program should behaved completly unpredictable.
A function can be designed in two ways:
The assert function in <assert.h> can be used to check that assumptions are followed.
 
if expression is false when
is executed, the assert macro will print a message on stderr , such as
It then calls abort() to terminate execution.
 
Example:
If NDEBUG is defined at the time <assert.h> is included, the assert statement is ignored. This can be used to remove assertions in the final product.
3.5.3.3 Exception
To avoid cluttering the code with handling of abnormal conditions, exceptions may be used.
Always put a catch all statement around your main procedure, to be prepared to save the users work before exiting the program.
Never throw an exception within a destructor because it is not well defined if the object gets deallocated if the exception thrown.

3.5.4 Memory allocation

Use the operators new and delete . The old C routines malloc() and free() is not recommended to use.
All objects allocated with new must be deallocated with delete before the object falls out of scope.
When arrays are deallocated, you must use the notation:

3.5.5 Boolean expressions

Conditional expressions in e.g. if -statements are defined in terms of integer tests against zero. You may choose to utilize this fact, or not to utilize it. Strive for readability, as shown in the following examples::
 
Use 
Do not use 
if (ptr)...
if (ptr !=NULL)... 
if (myInt != 0)...
if (myInt) ... 
if (myFlag)...
if(myFlag==true)... 
 
 

3.6 Type conversions

C++ is a strongly-typed language, and you should always try to stay inside the type system. However, sometimes a type conversion is needed, and this can be done in different ways.
C++ has an explicit conversion operator, but it is very flexible and allows many dangerous conversions. Alternatively, implicit type conversion can be used if it has been declared for the involved datatypes. Implicit conversion is usually type-safe, but it can be hard to understand.
Unfortunately, some built-in implicit arithmetic conversions can be dangerous. For example, if one unsigned integer and one signed integer are added, both arguments are first implicitly converted to unsigned integers. For negative numbers, wraparound is used, so this will result in a very large number.
It is better to use an already existing implicit type conversion than explicit type conversion, if you have to use a conversion at all. Implicit type conversions are more safe, even if there are some problems with arithmetic types.
But do not declare own implicit type conversions, unless really motivated. It is better to define wanted conversions as functions, and call them explicitly as any other function.
In summary, when you need a type conversion, try the alternatives in the following order:
  1. Explicit conversion function
  2. Implicit conversion operator
  3. Explicit conversion operator
Always use the new type conversion operators ( dynamic_cast , static_cast , reinterpret_cast , const_cast ) when possible.
If necessary use the run-time type information instead of an own variable to distinguish the type.
A typedef can be used if the type is more complicated, for example pointers to functions.
Use type conversions with care. Think twice if they really are needed. Don't use it as a quick solution to get around a complaint from the compiler. A virtual function can often provide a type-safe alternative to type conversion.
However, explicit type conversion may have to be used in these cases:
A derived class object can be assigned to a base class object, without type conversions. This is called slicing. But data extensions made in the derived class will be removed during the assignment. If this is not your intention, use a pointer or reference to the class instead.
For a class that is publicly derived from a base class, there is always an implicit conversion defined from a derived class pointer to a base class pointer.

3.5.7 Loop constructs

The for statement should only be used in cases where one loop variable is incremented with a constant amount at each iteration, and where the termination condition is a constant expression. More irregular loop constructs should use the while and do-while statements. while is used when the loop is executed zero or more times, whereas do-while is used when the loop always is executed at least once.
The loop variable should always be defined inside the for-statement. If several for-statements are used after each other, each of them should use a new loop variable. The loop variable should not be modified inside the loop.
Infinite loops should be written as
The following notation must not be used:
Among common programming errors, the hardest to find are usually range errors, also called off-by-one errors. The following programming technique makes these errors less likely:
 

Express a range by the first element of the range and the first element beyond it.

Use inclusive lower bounds and exclusive upper bounds. In other words, instead of talking about values of x with x>=16 and x<=37, talk instead about values with x>=16 and x<38. Using these conventions, the following is valid:

Example:
Another way to think of asymmetric bounds is to realize that they represent first occupied and first free elements of some sequence. This way of looking at things is particularly useful when dealing with buffers of various sorts.

3.5.8 Break and continue

break is used to leave the innermost loop. If using break means that you can get rid of flag variables, it is a good use of it.
 
Good 
Less good 
do 
    { 
    if ( ) { ... break; } 
    } while (cond)
do 
   { 
    if ( ) {... endFlag = true;} 
    } while (cond && !endFlag) 
 
continue means leave the current iteration of the loop. We do not recommend the use of continue . An if-statement makes the code more clear.
 
Good 
Less good 
while (...)
    { 
    if ( ) 
        {...} 
    else 
        {...} 
    }
while (...) 
    { 
    if ( ) 
        { ... continue; } 
     ... 
     } 
 

3.5.9 Switch

All possible alternatives must be taken care of in a switch statement. Either enumerate all alternatives (for enumeration types) or supply a default alternative. If the default alternative is not an expected case, it should signal a fatal error. The default case always should be last.
Do not allow a block of code associated with one case label to fall through to the next case label. Each block of code must end with " break; ". However, you may associate several case labels that have no intervening code with one block of code.

3.5.10 Input / Output

Use the iostream classes for I/O ( <iostream.h> , etc.), and not the old C routines like printf ( <stdio.h> ).
There are two categories of text strings: internal strings and language-dependent strings. Language-dependent text strings should not be hard-coded in the programs. It should be possible to change the language of all texts without having to recompile all source code. At most linking should be needed. More information about this will be supplied later.
Always prepare for use of 16-bit Unicode character set.

3.6 Preprocessor directives

#define should never be used to define constants or macros. Use const declarations and inline functions instead.
Do not use preprocessor definitions to change the language syntax. We are using the language C++, and we should not hide it by defining keywords begin and end to make it look like Pascal.

3.7 Portability

We should design our applications in such a way that it is easy to port them to different computer architectures.
Isolate parts that depend on the internal representation of integers, floating-point numbers, and character set.
Avoid type conversions, since they can cause porting problems.
Remember that the order of evaluation within an expression is not defined by the language, except for a few operators like ||, && and the comma "," operator.
Encapsulate calls to the operating system and other external parts in the environment. Use ANSI C, ISO C++, VxWorks, POSIX, X/Open, or UNIX95 compliant routines, if possible.

3.8 Comments

All comments should use the C++ comment sign // , even if the comment is a block of text occupying several lines.
Example:
Avoid using the C comment /* . . . */ . It might be used for outcommenting a piece of source code that for the moment is not needed, but it is better to use

" #if 0 . . . #endif " for that purpose. Add a comment with a text that motivates why the code is outcommented.

If you want to use a comment on the same line as a preprocessor directive, for example #include , you should also use the standard C comment, since the C++ comment usually is unknown to the preprocessor.

All comments must be written in English.

Each source file must begin with a comment stating what the declarations in it have in common, references to manuals, general hints for maintenance, copyright text, etc. This comment block should have the following format:

//
// FILE <file>
//
// $Id: CPPGuideline.htm,v 1.2 1998/05/05 05:50:46 icrnkovi Stable $
//
// DESCRIPTION <short description, 1 line>
//
// AUTHOR <dept> <name>
//
// All rights reserved. Reproduction, modification, use or disclosure
// to third parties without express authority is forbidden.
// Copyright <Company name>, <Country>, <Year>.
//
// HISTORY:
//
// $Log: CPPGuideline.htm,v $
// Revision 1.2 1998/05/05 05:50:46 icrnkovi
// Draft changed to Approved
//
// Revision 1.1 1998/04/02 12:22:33 edewaal
// Initial Version
//
//
// ABSTRACT
// <description text>

//*/

 
 

A skeleton for this comment block can be copied from

$SDE_HOME/forms/c++_head.skl

Consult your PC-Admin for the location in the Windows NT environment.

In the class declaration, precede each member function with one or two comment lines describing the function. If more comment lines are needed, it is usually better to place them in a general comment block before the class.

For simple classes, it may be enough to use a single comment line for a group of related member functions.

Note that the comments in the include file are intended for the user of the class, whereas the comments in the implementation file are directed to the maintainer of the class. The comments should not be identical.

In the implementation file, each nontrivial function should have a comment stating its purpose, the algorithm used (unless it is obvious), and something about the assumptions it makes about its environment.

Also use comments within the function body to describe the semantics (the flow) of a function, and during coding to mark things that need to be checked or may require improvement. An easy way to find passages to check once more is to mark them with " @@@ " in a comment.

Further, add comments in places where the code is nonobvious and/or nonportable.

Very little else should be needed. The functions should be that small that the comment at the beginning of the function should be sufficient.

Remove all trivial comments. If comment templates are used, remove lines that are not applicable, instead of adding text like "Not applicable", "Not used", "None", etc.

4. Formatting Issues

Contents

4.1 Upper/Lower case
4.2 Spaces
4.3 Blank lines
4.4 Indentation
4.5 Structured statements
4.6 Line breaks
4.7 Comments
4.8 Semicolons
4.9 Commas
 
These issues frequently lead to a lot of discussions. An way to avoid such discussions is to use a source code formatting tool, like for example $SDE_HOME/bin/indent .
During maintenance of a piece of code, use the same formatting style as it was originally written in, even if the style breaks the formatting rules.

4.1 Upper/Lower case

The use of upper and lower case letters is described in Naming conventions .

4.2 Spaces

Binary operators should be separated with one space on each side.
But no space before a comma, only after it.
The semicolon must also have a space after it. This is important in the for-statement.
Unary operators are written directly together with the operand, without a separating space (also the not operator " ! "). Only operators that are keywords ( new and delete ) should be followed by a space.
A function name is written directly together with the following left parenthesis, both in a function declaration and in a function call.
In parenthesized expressions with several levels, spaces may be used to clarify the grouping of the parentheses.

4.3 Blank lines

Separate logical sections in the code by blank lines. Blank lines are important to enhance the readability of the code.
Do not use the Form Feed character. The page divisions seldom occur at a wanted places, especially not after maintenance. Use blank lines instead.

4.4 Indentation

Use two spaces for each indentation level.
Use of tab characters is not recommended.

4.5 Structured statements

Matching braces shall be positioned in the same column. This makes it much easier to see the correct structure. The only exception is one-line definitions, where the two braces may be written on the same line.
The opening brace is written on a separate line.
The closing brace must be written in the same column as the opening brace.
Preferrably, indent the opening brace and then keep the code margin in the same column as the enclosing braces, as shown to the left below. Alternatively, you may instead keep the brace in the same column as the line above it, and instead indent the code enclosed by the braces, as shown to the right below (class example only).
Anyway, always use the same indentation rules within the whole file.
Class declaration:
 
class Class 
    { 
    public: 
        Class(); 
        . . . 
    protected: 
    . . . 
    private: 
    . . . 
    };
class Class 
    { 
         public: 
            Class(); 
            . . . 
        protected: 
            . . . 
        private: 
            . . . 
     }; 
 
Function definition:
 
void print(int value) 
    { 
    doSomething(value); 
    . . . 
    } 
 
 
 

if-statement:
 
 
if (expr) 
    statement; 
 
if (expr) 
    statement; 
else 
    statement; 
if (expr) 
    { 
    statement; 
    } 
else if (expr) 
    { 
    statement; 
    } 
else 
    statement; 
 
 
 

switch:
 
switch (expr) 
    { 
    case literal: 
        statement; 
        statement; 
        . . . 
        break; 
    case literal: 
        . . . 
    default: 
        . . . 
    } 
 
 
 
for:
 
 
for (int counter = start; counter < end; counter++) 
    { 
    statement; 
    . . . 
    } 
 
 
 
while:
 
while (expr) 
    { 
    statement; 
    . . . 
    }; 
 
 
 
do:
 
do 
    { 
    statement; 
    . . . 
    } while (expr); 
 
 

4.6 Line breaks

Each statement should be placed on a separate line.
A source code line should not be longer than that it fits in an editor window and on A4 paper, portrait orientation, when printed. Of course, this depends on the selected font size. A recommended maximum is 80 (-100) characters.
If one statement is longer than one line, separate it at a logical position, and the continuing lines must be indented compared with the first line.
Each file should end with a line terminator (even if this is not always required on Unix).

4.7 Comments

Place comments as close as possible to the code to which they refer. Comments are often incorrect and need to be checked against the code.
Normally, a comment line should be placed immediately before the corresponding code lines, or if it is short, at the end of a code line.
Blank lines can make it easier to see to which code lines a comment should be associated.

4.8 Semicolons

All simple statements must be terminated by a semicolon. This is required by the language.
Compound blocks {} should never be followed by semicolon, except after a declaration of a class, struct, union, enum, or aggregate, where it is required by the language.
In particular, never use a semicolon after a function definition.
The reason a semicolon must be used after type declarations is that C++ allows an object name to follow after the block, before the semicolon. But this possibility should never be used (except in typedef declarations).

4.9 Commas

Commas can be useful when you want to indicate that two statements after each other should be kept togehter, or in situations where you want to do several things, but only want one result. However, commas should be use restrictivly. Example of usage:
 

5. Object Orientation Issues

Contents

5.1 Introduction
5.2 Use of inheritance
5.3 Interfaces
5.4 Use protected or private members?
5.5 Aggregation as alternative to inheritance
 

5.1 Introduction

There are many different definitions of Object Oriented Programming. One general definition is:
This means that there should be a close mapping between the abstract model of a system, and the actual implementation of it. Objects in the model of a system should correspond to objects in the program.
A commonly used language-oriented definition is:
Data abstraction is the way of encapsulating data, so that it only can be accessed through an interface consisting of access routines. Data abstraction can be simulated in any language, using strong programmer discipline, but a C++ class supports encapsulation that makes it impossible to reach data without using the interface.
Inheritance is the ability to relate classes to each other. A class can inherit members from another class, redefine some of the members, and also add some new members. Inheritance can be used for two different (and sometimes conflicting) purposes:
Polymorphism means that values and variables can be interpreted to be of more than one type. In C++, an object reference has both a static type that is used in the declaration, and a dynamic type that can change during the execution. The dynamic type is always a subtype of the static type.
Dynamic binding means that it is the dynamic type, not the static type, that decides which class members to use, in the case where inherited members have been redefined in derived classes. This means that the user of an object does not have to know exactly what variant the object is of.
In C++, dynamic binding must be explicitly requested with the keyword virtual , and the class object must be accessed via a reference or pointer.

5.2 Use of inheritance

Abstraction, and especially data abstraction, is an important technique for developing programs that are reasonable easy to maintain and modify as requirements change. Data abstractions are particularly important because they hide complicated things (data structures) that are likely to change in the future. Data abstraction permit the representation of data to be changed locally without affecting programs that use the data.
Inheritance and dynamic binding are useful, but their importance is not as fundamental as that of data abstraction.
One problem with inheritance in C++ (and in almost all other object-oriented languages) is that it compromises data abstraction to an extent. When inheritance is used, a data abstraction implementation has two kinds of users. There are the "outsiders" who simply use the objects by calling the operations. But in addition there are the "insiders". These are the derived classes, which typically are permitted to violate encapsulation. When encapsulation is violated, we lose the benefits of locality. We must consider the combined code of the base and derived classes in reasoning about the class, and if a base class needs to be reimplemented, we may need to reimplement its derived classes too.
To reduce this problem, C++ allows you to make the implementation private , in which case it is hidden also for derived classes, whereas protected opens it for subclasses.
C++ implements inheritance and virtual functions in a very efficient way compared with most other object-oriented languages. But in many of our applications, we still cannot ignore that there is a performance penalty of using dynamic binding.
Also, declaring virtual functions means that the compiler lays out virtual function tables, that in some cases may occupy a lot of space.
It is not obvious how inheritance affects the maintenance of a program system. Inheritance used in a sensible way can reduce future maintenance effort. But used in a less well-considered way, it can cause the classes to be strongly coupled and hard to maintain. If you feel uncertain about how to use inheritance, leave it out. Use of data abstraction is fundamental and must never be compromised. Inheritance and dynamic binding can add a flexibility to data abstraction that is sometimes needed, but it takes time and experience to master it.
Whether inheritance makes a system easier to understand is not obvious either. Inheritance may add structure to a system, but a deeply nested class hierarchy may be difficult to comprehend.

5.3 Interfaces

The most important aspect of object-oriented programming is to have well-defined interfaces. With COM this has been taken one step futher, where each interface are immutable and individually identified with a GUID.
When designing, coding, and documenting a class, keep in mind that the class has three different kinds of users:
A user of a class must not need to read through a thick document with implementation details in order to find out how the class is to be used. Ideally, for each class a separate document (or manual page) is written, for each of the mentioned user categories:
Note that the public and protected interfaces are very important also for the maintainer of a class. They define the degree of freedom for implementation changes. As long as the external behaviour via these interfaces are unchanged, the class maintainer can do any reimplementation of the class internals.

5.4 Use protected or private members?

Classes in an inheritance hierarchy are often designed together. Here the design team can for each class decide whether to use protected or private members, since they have the total picture and know what is needed.
But inheritance is also said to allow future classes to incrementally change the behaviour of earlier written base classes. However, this requires that the base classes have declared a protected interface.
So, should the programmer declare protected members just in case anyone may want to create derived classes in the future? The short answer is no. To open the implementation can create a dangerous hole in the secure encapsulation of a class. A previously well-tested class can stop working because a derived class (written by another programmer) is manipulating the internal data of the class.
Use the following methodology as a guideline: Normally, do not open the internal members (data or functions) to derived classes. That is, declare the members private . If you decide to export a protected interface, though, you have to document the interface so well that the writer of the derived class can do the job without having access to the source code of the base class. Documenting this interface is easier to do if only functions are exported, and no data members.

5.5 Aggregation as alternative to inheritance

Often aggregation is a better alternative than inheritance. Instead of inheriting from a class, you can declare a member object of that class and only use its public interface. This way you are not affected by changes in the protected interface of that class.
Instead of
declare
Put simply, inheritance means "is", and aggregation means "has".

Inheriting is a more committing decision than becoming a client. When you inherit you gain access to base class implementation, which gives you more power but no protection from implementation changes.

Only use public inheritance if your derived class really "is-a" base class. The derived class must be allowed at all places where the base class is expected.

6. Source Code Review

All source code shall be reviewed before it is released. The reviewers check that the code follows the style guide and is simple enough to be maintained later on. Source code reviewing is described in the following document:
3BSE0009790
Code Review

7. Check List

Recommendations about how to use C++ cannot easily be described in some simple rules. We therefore strongly recommend that you read the whole style guide. For reference, however, the most important rules are summarized here together with their corresponding section numbers.
Main philosophy:
Rules and guidelines: