|
C++ Coding Standards |
|
| |
You can read and download this C++ Coding Standards coding style guide and code guidelines from here
for free at your own risk. All registered trademarks, product names and company names or logos mentioned
herein are the property of their respective owners.
C++ Coding Standards
Author: Brett Slocum
Date: 02/13/1997
- Introduction
This documentation describes coding standards using C/C++ in our environment. As with most standards and guidelines, this document is not fixed in concrete, but it is not a suggestion either. Programmers are required to follow these recommendatio
As such, the standards are not intended to create obstacles or encourage bureaucracy. Consequently, if programmers see revisions need to be made, it is their responsibility to review changes with the architect team so they can be incorporated in the s
If a particular situation arises where a good case can be made to violate the standards, discuss it with the architecture team and fully document the variance.
- File Organization
C++ source files should have a .cpp extension. Each source file should always have a header file with same name and a .h extension.
File names should be 31 characters or less. No embedded spaces should be used in file names. The name of the file should be the name of the class contained in the file without the leading "C".
Examples :
Valid filenames for the given classes:
Class Source File Header File
CCPRMolecule | CPRMolecule.cpp | CPRMolecule.h |
CCCAMyClass | CCAMyClass.cpp | CCAMyClass.h |
CCPRAtomRevision | CPRAtomRevision.cpp | CPRAtomRevision.h |
There should be only one primary class definition per file.
-
- Header Files
- Comment Block
The comment block in a header file describing the purpose and maintenance history of the file should follow the template below:
#ifndef CLASSNAME_H
#define CLASSNAME_H
///////////////////////////////////////////////////////////////////////////////
//
// $Header: $
//
// NAME: className
//
// BASE CLASSES: none
//
// PURPOSE: purpose description
//
// AUTHOR: Who created this originally
//
// COPYRIGHT NOTICE:
//
// (C) Residential Funding Corporation
// Created in 1997 as an unpublished copyright work. All rights reserved.
// This document and the information it contains is confidential and
// proprietary to Residential Funding Corporation. Hence, it may not be
// used, copied, reproduced, transmitted, or stored in any form or by any
// means, electronic, recording, photocopying, mechanical or otherwise,
// without the prior written permission of Residential Funding
// Corporation.
//
// REVISION HISTORY:
// $Log: $
///////////////////////////////////////////////////////////////////////////////
// Includes
// Constants
// Types and Classes
// ... rest of the file goes here
#endif // CLASSNAME_H
- Allowed Contents
Header files may contain:
- Type definitions
- Templates
- Function declarations
- Inline function definitions
- External data declarations
- Constant definitions
- Enumeration types
- Name declarations
- Include statements
- Comments
Examples :
Lexical Element |
Example of Code |
Notes/Comments |
Type Definition |
typedef float REAL; |
Define a type REAL which is a floating-point
number |
Template |
List<Atom> *resatomlist; |
Example of class data using templates |
Function Declaration |
Atom *GetAtom( Atom *atom); |
A class member function declaration |
Inline Function Definition |
inline char* CResidue::GetResidueName() {return residuename;} |
An access function for class Residue |
External Data Declaration |
extern CString sName; |
|
Constant Definition |
const float pi = 3.14159; |
The keyword const redefines the word pi to mean a floating-point
number whose value is set to 3.14159 |
Enumeration |
enum EColor(red, green, blue}; EColor eColor; |
Thus, we could specify: eColor=green; to set eColor |
Name declaration |
|
|
Include statement |
#include "Molecule.h" |
The quotes indicate a user-defined header
file is to be included. |
Comment |
// This is a comment line |
The C++ comment characters (//) should be used in preference over the old style (/* and */) comment characters. |
They may not contain:
- Ordinary function definitions
- Variable definitions
- Constant aggregate definitions
- External references to global variables
-
- Guard Code
Header files will define and test a preprocessor variable preventing multiple inclusion of that header file in the same translation unit:
#ifndef RESIDUE_H
#define RESIDUE_H
...rest of code...
#endif
- Include Statements
Each header file will contain #include statements for all header files on which it depends:
#include "header_file.h"
or
#include <header_file.h>
for default (compiler-default) header files.
Do not include pathnames in the #include statement. Use the Tools/Options menu in Developer Studio to add directories to the search path.
Do not create order dependencies among header files for classes that reference each other. Specify forward declarations for each class to avoid this situation.
Do not group forward references into a single header file. While this has some organization appeal, it results in superfluous class declarations.
-
- Source Files
- Comment Block
The comment block in a source file describing the purpose and maintenance history of the file should follow the template below:
///////////////////////////////////////////////////////////////////////////////
//
// $Header: $
//
// NAME: className
//
// BASE CLASSES: none
//
// PURPOSE: purpose description
//
// AUTHOR: Who created this originally
//
// COPYRIGHT NOTICE:
//
// (C) Residential Funding Corporation
// Created in 1997 as an unpublished copyright work. All rights reserved.
// This document and the information it contains is confidential and
// proprietary to Residential Funding Corporation. Hence, it may not be
// used, copied, reproduced, transmitted, or stored in any form or by any
// means, electronic, recording, photocopying, mechanical or otherwise,
// without the prior written permission of Residential Funding
// Corporation.
//
// REVISION HISTORY:
// $Log: $
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Includes
// Constants
// Externals
// Types and Classes
// Variables
// Member Functions
- Include Statements
Only those header files needed directly by the source file should be included.
For Visual C++, StdAfx.h should be included first for the proper creation of precompiled headers. No other code, except for comments can precede the StdAfx.h include.
- Function Comments
Each function (excluding Get and Set access functions and Operators) must have a header that includes the following elements:
- NAME
- Name of the function
- DESCRIPTION
- Describes the purpose, usage and other important information concerning this function. Any precondition should be specified here. Note: Do not describe how the function does its job. That's what the source code is for. One doesn't wa
- PARAMETERS
- A list of each of the parameter names and a brief description of that parameter. The name of the parameter should be followed by one of three
- RETURN
- The type returned and a brief description of that object. Include any requirement for this return type (e.g. must release the memory).
- SIDE EFFECTS
- Anything that might happen that would not normally be expected by a user.
Example:
///////////////////////////////////////////////////////////////////////////////
//
// NAME: open
//
// DESCRIPTION: opens the specific file.
//
// PARAMETERS: path(0) - full pathname for the file to be opened
//
// RETURN: CStatus - Boolean, indicates success or failure
//
// SIDE EFFECTS: none
//
///////////////////////////////////////////////////////////////////////////////
BOOL CStatus open(char *path)
{
function body….
}
Source Code Style
- Naming Conventions
Do not rely on upper and lower case differences to yield unique names.
- Word separators
Separate words in identifiers by capitals, not underscores.
- Prefixes
Variable names should be prefixed by Hungarian notation. More than one prefix may apply (e.g. a pointer to an exception object could be named pxPricingException). Regular prefixes include:
Prefix |
Data Type |
a |
array |
b |
BOOL |
by |
unsigned char (BYTE or
UCHAR) |
c |
char |
C |
class |
d |
double |
dw |
DWORD (unsigned long) |
e |
enum variable |
E |
enumerated type |
f |
float |
h |
Windows handle |
i |
int or short |
l |
long |
m_ |
class member variable |
n |
int |
p |
pointer |
s |
CString |
sz |
zero-terminated char
string |
u |
unsigned int |
v |
void |
w |
WORD (unsigned word) |
x |
C++ exception
object |
- Names
Names of enumerated types and classes should begin with an uppercase letter (either E or C, respectively), whereas names of variables and constants should begin with a lowercase letter. Use the Windows standard of starting functions with an upperca
Names should indicate the meaning of the variables, rather than their type. Names should be descriptive and use complete words. Avoid the use of initial or trailing underscores. Use multiple words – but use them carefully. It can be hard to remember
Similarly:
Correct | Incorrect |
szDateDue | szDueDate |
szNameFirst | szFirstName |
eColorRed | eRedColor |
iVolumeMax | iMaxVolume |
bStatusEnabled | bEnabledStatus |
Naming variables this way means that related variables tend to sort together, making cross-reference listings more useful.
Aesthetics
- Indent size
Use four spaces. Do not use tabs because different editors and printers deal with tabs differently. These parameters can be set in the Tools/Options menu of Visual C++.
Braces
Braces shall follow an indented block style in which opening and closing braces are each placed on separate line lined up vertically with the control statement. The body of the code within braces is indented by one indent level.
Example :
while (iLoopingCount)
{
doSomeThing();
}
Line length
Line length should not exceed eighty characters.
Statements
Do not use multiple statements per line.
Example :
// Incorrect
a = b; c++; d = GetMax();
// Correct
a = b;
c++;
d = GetMax();
Class definitions
Put class names against the margin.
Example :
WORD
class::func()
{
doSomeThing();
}
Spaces
Use lots of spaces, except around ‘.' or ‘->' or between unary operators and operands.
Pointer modifiers "*" and reference modifiers "&" in declarations are grouped with the type, not the variable. Overloaded operators immediately follow the key word operator, with no intervening white space.
Example :
char* p = "hello";
Complex operator+ (const Complex& op1, const Complex& op2);
Comments
Comments should be used liberally to explain what code is doing and why, but should not duplicate information readily discernable in the code itself. Comments are meant to be read by others, so liberal use of white space and blank lines will incre
Use C++-style comments (i.e. // ), except where commenting out part of the middle of a line (such as unused formal parameters). There should be one space between the double slash and the start of the comment text.
Example:
Unsigned draw_rect(unsigned wWidth, unsigned wHeight /*, unsigned wDepth */)
- Block Comments
Block comments should have blank lines above and below. A block comment is a comment containing two or more lines.
Examples :
// This is a C++ block comment.
// It has two lines in it.
// This is a single-line comment
- End of Line Comments
End of line comments are small annotation tacked on the end of a line of code. They are focused on one or a very line of codes where block comments refer to larger sections of code.
Example :
IEmployeeCur = iEmployeeCur + 1; // Keep the index pointer
// synchronized for OLE clients
Constants
Do not use #define to declare constant values. Const or enum should be used instead.
Example :
const WORD wMode;
Use enumeration for a set of related constants rather than declaring them separately.
Example :
// Incorrect
const unsigned int iCOLOR_RED = 0;
const unsigned int iCOLOR_GREEN = 1;
const unsigned int iCOLOR_BLUE = 2;
// Correct
enum EColor {eRed, eGreen, eBlue};
Do not repeat const
declarations in both source and header files. If it is declared in the header file, it is meant for other modules to share. If it is declared in the source file, only that module is meant to use it.
Use constant variables, instead of literal constants, especially if used in more than one place.
Example :
// Incorrect
int iID = 1000;
// Correct
In the .CPP file:
int iID = iStartingID;
In the .h file:
const int iStartingID = 1000;
Variables
Limit the scope of variables as much as possible.
Each variable is to be declared in a separate declaration statement.
Examples :
// Incorrect
CString sFilename, &rsDirectory;
// Correct
CString sFilename;
CString& rsDirectory;
Define variables close to where they will be used.
Always initialize variables. Declare and initialize variables no earlier than the point at which their initialization values are known.
Avoid local variable names that are identical to the names of variables with greater scope.
Beware of the variable's scope. The variable will often remain in existence longer than originally anticipated.
- Global variable definitions
Global variables are strongly discouraged.
Do not use global variables that only have module (throughout the current source file only) scope.
Classes
Use struct when there are no private or protected member data and no member functions (including constructors and destructors). In other words, use struct for public data structures. Use class in all other cases.
Member data and functions should be declared in the following order: public members, protected members, and private members. Always use the access keywords public, protected, and private.
- Encapsulation
Member data in a class should be declared private, not protected or public. This is to adhere to the object-oriented design principle of data encapsulation. If other classes need access to member data, provide get and
Hide member functions that are not required for the external interface using the private or protected
keyword.
Localize and hide design decisions that may change.
- Inheritance
When using inheritance, specify public for each parent class.
Example :
class myClass
{
// ….
}
class derivedClass : public myClass
{
// ….
}
In Visual C++, classes that have no other parent class should be derived from CObject. This allows these classes to be part of templated collection classes.
Avoid multiple inheritance.
- Member Functions
Keep function length to less than two pages. One page is better. Long functions are more difficult to understand and maintain.
Do not use variable length argument lists (similar to printf).
Large function parameters should be passed by reference, not by value. If the function is not changing the value of a large parameter, declare that parameter const. Small parameters that won't be changed by the function can be passed by value.
Whenever an address is returned from a member function, it should be returned as a const pointer or a const
reference, unless you want the user to be able to change the contents of what the pointer de-references.
- Inline Functions
Inline functions should not be used, unless performance is known to be a problem. The exception to this rule is access functions, which should be inline, but not within the body of the class.
Constructors and destructors should not be inline.
Inline functions should be three lines long or less.
Example :
// Incorrect
class CPriority
{
public:
int CPriority() // should not use inline here
{
doSomething();
};
int getx();
private:
int x;
};
int CPriority::getx() // inline should be used here
{
return(x);
}
// correct
class CPriority
{
public:
int CPriority(); // constructor should not use inline
int getx();
private:
int x;
};
// constructor
int CPriority::CPriority()
{
doSomeThing();
}
inline int CPriority::getx () // get function can use inline
{
return(x);
}
- const
Member Functions A member function that does not affect the state of an object (its member data) is to be declared const. Member functions declared as const may not modify member data and are the only functions that may be invoked on const obj
- Constructors and Destructors
Initialize member data in the class constructor. Pointers should be set to 0 in the constructor and deleted in the destructor.
Constructors should use initialization, not assignment, to set member data, if possible.
Example :
// Incorrect
Catom::Catom(int iAtNum, float fAtWt)
{
m_iAtomicNumber = iAtNum;
m_fAtomicWeight = fAtWt;
}
// Correct
Catom::Catom(int iAtNum, float fAtWt) : m_iAtomicNumber(iAtNum),
m_fAtomicWeight(fAtWt)
{
; // Empty
}
A class that uses the new keyword to allocate objects managed by the class must define a copy constructor. This is to avoid surprises when an object is initialized using an object of the same type. If an object manages the allocation an
Example :
// Incorrect
class String
{
public:
String( const char* cp = ""); // constructor
~String(); // destructor
private:
char *sp;
};
String::String(const char* cp) : sp ( new char[strlen(cp)] ) // constructor
{
strcpy(sp, cp);
}
String::~String() // destructor
{
delete sp;
}
void
main()
{
String w1;
String w2 = w1;
// Warning: the destructor for w1::sp will be called twice:
// first when w1 is destroyed; again, when w2 is destroyed.
}
// Correct
class String
{
public:
String( const char* cp = "" ); // constructor
String( const String& sp ); // copy constructor
~String(); // destructor
// …
private:
char *sp;
// …
};
String::String( const String& stringA ) : sp( new char[strlen(stringA.sp)] )
{
strcpy ( sp, stringA.sp );
}
String::~String() // destructor
{
delete sp;
}
void
main()
{
String w1;
String w2 = w1; // SAFE COPY: String::String( const String& ) called.
}
All classes that are used as base classes and have virtual functions, must define a virtual destructor.
Avoid the use of global objects in constructors and destructors.
- Operator Overloading
Use operator overloading sparingly and in a uniform manner. One disadvantage in overloading operators is that it is easy to misunderstand the meaning of an overloaded operator
- Friends
Use friend functions sparingly. Friends offer an orderly way of getting around data encapsulation for a class. A friend class can be advantageously used to provide functions that require data that is not normally needed by the class
Flow Control Structures
A break statement must always terminate the code following a case label.
A switch statement must always contain a default branch that handles unexpected cases.
If testing the value of a single variable, use a switch statement instead of an if statement.
The flow control statements if, while, for and do should be followed by a block, even if it is empty or contains a single line.
Example :
//Incorrect
while ( !bSomething ) ;
for ( i = 0; i < 10; i++ )
cout << aSomeArray[I];
//Correct
while ( !bSomething )
{
; // Empty
}
for ( i = 0; i < 10; i++ )
{
cout << aSomeArray[I];
}
Do not embed assignment statements into flow control statements.
Example :
// Incorrect
if ( bFlag = GetStatusWrite() )
{
...
}
// Correct
bFlag = GetStatusWrite();
if ( bFlag )
{
...
}
Never use goto. Goto breaks the control flow and can lead to code that is difficult to comprehend.
Avoid the use of continue. Continue can be used to exit from loops. However, code may be more comprehensible by using an else clause instead.
- Boolean Tests
Only use if (something) and if (!something) when something is a BOOL value. If it is a multi-valued variable or a pointer, test for equality with 0.
Examples :
if (!strcmp( myString, "value" )) // Bad
if (strcmp( myString, "value" ) == 0) // Good
- Control Statement Overrides
Minimize the use of return, break, continue and goto statements which override normal control statement structure. However, each of these statements has a place - use them when the code would be significantly more convo
Additional Guidelines
- Debugging
Test the input parameters of a member function for valid data using the assert
function.
Exceptions
Catch exceptions thrown by MFC functions. Use a try-catch block around calls to these functions.
Example :
CRecordSet clPriceData;
try
{
clPriceData.Open();
}
catch (CDBException* pxOpen)
{
pxOpen.
}
Unions
Avoid unions, as they defeat strong type-checking and are very difficult to verify in a debugger.
Use C++ memory management
Do not use realloc. Malloc, calloc, and free should be avoided; the operators new and delete should be used instead.
Always use delete whenever memory was allocated by the new operator. In order to help eliminate the problem of accessing memory that has been freed, the pointer must be assigned to 0 after the pointer has been deleted. Note: there are in
Input / Output
Use the C++-style iostream class, not printf, fprintf, or other C-style input/output routines.
MFC
Use the BOOL and CString classes provided by MFC, not int (for boolean responses) or char* types from C.
Use MFC classes where appropriate.
Null Pointer
The C++ language definition unequivocally defines a pointer value of 0 (zero), or any expression that evaluates to zero, as a null pointer. Stroustrup writes, "A constant expression that evaluates to zero is converted to a pointer, commonly c
References
- C++ Coding Standard, Apertus Enterprise Systems
Group, January 1997.
- C/C++ Programming Guide, Hassan Kaganda, Deluxe
Corporation, January 1997.
- Programming in C++, Rules and Recommendations, Mats Henricson and Erik Nyquist, Ellemtel Telecommunications Systems Laboratories, April 1992.
- Glossary
abstract base class
A class from which no objects may be created; it is only used as a base class for the derivation of other classes. A class is abstract if it includes at least one pure virtual member function.
access function
A function that set or returns the value of member data.
accessor
See access function.
catch clause
The exception handler executed when an exception is raised. The definition of an exception handler begins with the keyword catch.
class
A user-defined data type that consists of data elements and functions that operate on that data. In C++, this may be declared as a class; it may also be declared as a struct or a union.
class template
Definition of a family of classes. A new class may be created from a class template by providing values for a number of arguments. These values may be names of types or constant expressions.
compilation unit
The source code (after preprocessing) that is submitted to a compiler for compilation.
constant member function
A function that may not modify member data.
constructor
A function that initializes an object when it is created.
copy constructor
A constructor in which the first argument is a reference to an object that has the same type as the object to be initialized. A copy constructor is used to initialize an object to the same value as another object of the same type.
default constructor
A constructor that needs no arguments. The compiler often automatically generates a default constructor for a class, if the programmer does not define one.
destructor
A function called when an object is destroyed.
enumeration type
An explicitly declared set of symbolic integer constants. In C++ it is declared as an enum.
exception
A run-time program anomaly that is detected in a function or member function. Exception handling provides for the uniform management of exceptions. When an exception is detected, it is thrown (using a throw expression) to the exception handler.
forwarding function
A function that does nothing more than call another function.
function template
Definition of a family of functions. A new function may be created from a function template by providing values for a number of arguments. These values may be names of types or constant expressions.
identifier
A name that is used to refer to a variable, constant, function or type in C++. When necessary, an identifier may have an internal structure that consists of a prefix, a name, and a suffix (in that order).
inline function
A function declared with the inline keyword that gets directly replaced by its statements at compile time. Also, any member functions declared within the body of a class are automatically inline.
iterator
An object which, when invoked, returns the next object from a collection of objects.
macro
A name for a text string which is defined in a #define statement. When this name appears in source code, the compiler replaces it with the defined text string.
member data
Data identifiers defined within a class.
member function
Functions defined within a class.
overloaded function
A function name that is used for two or more functions or member functions having different types.
overridden member function
A member function in a base class that is redefined in a derived class. Such a member function is declared virtual.
predefined data type
A type defined in the C++ language, such as int or
float.
private member
The member data and member functions of a class that are only accessible within the class.
protected member
The member data and member functions of a class that are only accessible within the class and its derived classes.
public member
The member data and member functions of a class that are accessible everywhere.
pure virtual function
A member function for which no definition is provided. Pure virtual functions are specified in abstract base classes and must be defined (overridden) in derived classes.
reference
An identifier that refers to another identifer directly. In C++, the ambersand (&) is used immediately after the data type to indicate that the declared variable, constant, or function argument is a reference.
scope The context of a program in which an identifier exists.
structure
A user-defined type for which only public data is specified.
typedef
A typedef declaration in C++ specifies another name for a data type.
user-defined data type
A type that is defined by a programmer in a class, struct, union, enum, or typedef definition or as an instantiation of a class template.
Want to format C++ source code automatically? Use SourceFormatX C++ Code Formatter to indent your C++ code files.
|