Notes On C++ Style

 

March 13, 1999
 
 

Scott B. Johnson


Table Of Contents

1 Comments
1.1 File Headers
1.2 Class Declarations
1.3 Function Definition Separators
1.3.1 Function Definitions
1.3.2 Member Function Definitions
1.3.3 Member Function Groups
2 Declarations
2.1 Variables
2.2 Constants
2.3 Functions
2.4 User Defined Types
2.4.1 Enumerated
2.4.2 Structures
2.4.3 Classes
3 Constructs
3.1 if - else - else if
3.2 do - while
3.3 switch - case - default
3.4 for
4 Indentation
5 Header Files
6 Code Reviews


Comments

In general, comments should be provided at the developer's discretion. Verbose symbol names, should be used and comments placed in the code where the structure is not obvious.

1.1 File Headers

All code files begin with a header block describing the name of the file, the author and a brief description of the contents.
////////////////////////////////////////////////////////////////////////////////
// file : string.hpp
//
// dumping ground for string utilities
//
// Author    Date    Description
// ------    ----    -----------
// D. Bowman 5/22/94 Creation
//////////////////////////////////////////////////////////////////////////////////

1.2 Class Declarations

A brief description is required for each class declaration in a header file. If only one class is being declared, the file header comment is adequate.
////////////////////////////////////////////////////////////////////////////////
// a simple class declaration
//
class CSimple {
  int
    m_iNumber;
}; // end class Simple

////////////////////////////////////////////////////////////////////////////////
// another simple class declaration
//
class CSimple2 {
  int
    m_iNumber;
}; // end class Simple

1.3 Function Definition Separators

Function definitions are separated using a comment line that signifies the function type. These include non-member, public member, protected member and private member functions. A comment describing the function name is placed at the end of each definition block. Groups of class member functions are separated using a comment block.

1.3.1 Function Definitions

////////////////////////////////////////////////////////////////////////////////
void
fnLower(char *pchString)
{
  for (int iIndex = 0; pchString[iIndex]; iIndex++) pchString[iIndex] = 
       tolower(pchString[iIndex]);
} // end fnLower()

////////////////////////////////////////////////////////////////////////////////
void
fnUpper(char *pchString)
{
  for (int iIndex = 0; pchString[iIndex]; iIndex++) pchString[iIndex] = 
       toupper(pchString[iIndex]);
} // end fnUpper()

1.3.2 Member Function Definitions

-- private member function separator--
//########################################################################
-- protected member function separator--
//************************************************************************
-- public member function separator--
//========================================================================
template <class DATA>
CList<DATA>::CList()
{
  m_pData   = 0; 
  m_pHead   = 0; 
  m_pNode   = 0; 
  m_pTail   = 0;
  m_bFailed = 0;
}  // end CList::CList()

//========================================================================
template <class DATA>
CList<DATA>::~CList()
{
  m_fnFirst();
  while (m_fnRemove());
} // end CList::~CList()

1.3.3 Member Function Groups

A brief description is required for each group of class member function definitions. If only one class of member functions are being defined, the file header comment is adequate. 

Declarations

The use of terse symbol names in declarations must be avoided. One or two character names are unacceptable. Multiple word names use the '_' character to represent a space delimiter. The use of word capitalization alone is discouraged.
int
  iDay_Of_Week, // encouraged
  iDayOfWeek;   // discouraged
Prefix name declarations with the following sequence. Write pointer names with the letter 'p' followed by the type prefix.
type prefix
char ch
unsigned char uch
char buffer sz
short int si
int i
long l
long int li
unsigned short int usi
unsigned int ui
unsigned long ul
unsigned long int uli
float f
double d
long double ld
usigned float uf
unsigned double ud
unsigned long double uld
bool b
functions fn
classes C
class templates C
class members m_
pointers p

2.1 Variables

Place variable declarations one line after the type specifier and indented by one tab. Multiple variable declarations of the same type follow, thereafter, one per line.
int
  iNumber = 0,
  *piNumber = 0;
long int
  liNumber = 0,
  *pliNumber = 0;
float
  fNumber = 0.0,
  *pfNumber = 0;
char
  chLetter,
  *pchBuffer1 = new char[256],
  szBuffer2[256];

2.2 Constants

When practical, constant name declarations should be used in place of preprocessor directive #define statements. Constant names appear in upper case. Variable style name prefixing is optional on constant names.
const int
  NUMBER = 500,
  BUFFER_WIDTH = 256;

2.3 Functions

Functions are prefixed with 'fn'. Function declarations (prototypes) will be placed in header files. If a function is designed for exclusive use within file scope, provide a static prototype prior to the function definitions.
...
static void
  fnLower(char *pchString);
...
void
fnLower(char *pchString)
{
  for (int iIndex = 0; pchString[iIndex]; iIndex++) pchString[iIndex] = 
       tolower(pchString[iIndex]);
} // end fnLower()
...

2.4 User Defined Types

The typedef naming mechanism is required for enum and struct based types. Type names created using typedef should appear in upper case.

2.4.1 Enumerated

Enumerated list members (which are constant by nature) should appear in upper case.
typedef enum {
  STATIONARY,
  FREE_FLIGHT,
  DIE,
  COLLISION
} EVENT;

2.4.2 Structures

typedef struct {
  float
    fx,
    fy,
    fz;
} VERTEX;
Formally naming a structure is only necessary during a linked list declaration. The typedef provided name is used thereafter.
typedef struct node {
  int
    iCount;
  char
    szBuffer[80];
  struct node
    *pPrev,
    *pNext;
} NODE;

NODE
  *pHead = 0,
  *pTail = 0;

2.4.3 Classes

Class names begin with a capital 'C' and may contain mixed case thereafter. Members of the class are prefixed with 'm_'. The member prefix is optional on user defined type declarations within the class. Public, private and protected keywords are aligned to the far left. Member data and function declarations are tabbed accordingly.  Classes that may encounter failure during instantiation require two additional public functions and two private variables.
 
bool
  m_fnFailed() {return m_bFailed;};
m_bFailed is set to true at the beginning of the constructor.  It is set to false when the constructor completes the instance initialization.
char
  *m_fnResult() {return m_pchResult;};
m_pchResult points to a message that describes the condition that resulted in failure.  This may also be used to indicate other failure results for other public and protected member functions.

template <class DATA>
class CList {
public:
  // public data
  DATA
    *m_pData;
  // public methods
  CList();
  ~CList();
  bool
    m_fnAppend(DATA *pData),
    m_fnFirst(),
    m_fnLast(),
    m_fnPrevious(),
    m_fnNext(),
    m_fnRemove();
  bool
    m_fnFailed() {return m_bFailed;};
  char
    *m_fnResult() {return m_pchResult;};
private:
  // private types
  typedef struct node {
    DATA
      Data;
    struct node
      *pPrev,
      *pNext;
  } NODE;
  // private data
  bool
    m_bFailed;
  char
    *m_pchResult;
  NODE
    *m_pHead,
    *m_pNode,
    *m_pTail;
}; // end CList


Constructs

Excessive use of opening and closing brackets is discouraged. Constructs that execute one line of code, should be written on one line. Constructs that execute two or more lines of code should appear within opening and closing brackets. The Classic C style (K&R) bracketing is preferred. If bracketing is used, a comment following the closing bracket is encouraged.
if (bState) return iSetting; // encouraged
...
if (bState) 
  return iSetting; // discouraged
...
if (bState) {
  cout << "\nNew Settings";
  return iSetting;
} // end if
else return 0;
...

3.1 if - else - else if

Closing bracket comment should state 'end if', 'end else' or 'end else if' depending on the construct
  if (!m_pNode) return 0;
  NODE
    *pNode = m_pNode;
  // head of list
  if (m_pNode == m_pHead) {
    // check for null list condition
    if (m_pNode == m_pTail) {
      m_pTail = 0;
      m_pHead = 0;
      m_pNode = 0;
    } // end if
    else {
      m_pHead = m_pHead->pNext;
      m_pHead->pPrev = 0;
    } // end else
  } // end if
  // tail of list
  else if (m_pNode == m_pTail) {
    m_pTail = m_pTail->pPrev;
    m_pTail->pNext = 0;
  } // end if
  // middle of list
  else {
    m_pNode->pPrev->pNext = pNode->pNext;
    m_pNode->pNext->pPrev = pNode->pPrev;
  } // end else
...

3.2 do - while

Closing bracket comment should state 'end while'.
...
while (m_fnRemove());
...
while (m_fnRemove()) cout << "\nremoving node";
...
while (m_fnRemove()) {
  cout <<"\nremoving node";
  iNode_Count++;
} // end while
...
do {
  cout << "\nremoving node";
  iNode_Count++;
} while (m_fnRemove()); // end while
...

3.3 switch - case - default

Closing bracket comment should state 'end switch'.
...
switch (m_chVariable[iIndex]) {
  case '\n' :
  case ' '  :
  case '.'  :
  case '\\'     :
    bContinue = 0;
    chTerminator = m_chVariable[iIndex]; 
    m_chVariable[iIndex] = '\0';
    break;
  default:
    cout << "\nno match";
} // end switch
...

3.4 for

Closing bracket comment should state 'end for'.
...
for (int iIndex = 0; iIndex < VARIABLE_ID_RANGE; iIndex++) {
  if (fnIs_Equal(m_chVariable, m_chVariable_ID_Text[iIndex])) {
    bSuccess++;
    m_pout->write(m_chVariable_ID_Value[iIndex], 
                  fnLength_Of(m_chVariable_ID_Value[iIndex]));
    break;
  } // end if
} // end for
...

Indentation

Indent code blocks using tab or space characters. Most IDE (Integrated Development Environment) editors provide a tab to spaces setting. A setting of two spaces for every tab is commonly used. 

Header Files

Nested header files are not permitted. The #include directive is only permitted within .cpp files. This prevents time wasted from tracking down where a type comes from and improves code compilation time.

Variable declarations within headers are not allowed. This prevents multiply defined symbols.


Code Reviews

The best method to ensure quality code development is through the use of code reviews. A code review consists of each member of the development team walking his/her fellow team members through the code they developed. Provide a printout of the code to team members prior to the review. Start by explaining the purpose of the code, how the C++ class or classes are structured and finally the specifics of the code. During this time other team members ask questions, point out style guide inconsistencies and provide suggestions to improve the code. Limit the review to a three hour session. This limit will prevent the review from slowing down and help prevent individuals from losing interest.

Conduct code reviews at least every two weeks. Pick a day of the week so that it becomes a habit for all team members. Exclude middle and upper management from the review, unless they have contributed code for review. In that event, show no mercy. 


Scott & Linda Johnson Homepage
Please feel free to mail us any comments or questions sjohnson@2036.net