C++ Language Coding Guidelines

Note: These coding guidelines are adapted from the book Computing Concepts with C++ Essentials by Cay S. Horstmann, published by John Wiley & Sons. They are copyright © 1997, by John Wiley & Sons. All Rights Reserved.  


Table of Contents

1. Introduction

2. Files or Modules

2.1. Source file layout
2.2. Header comment

2.3. Included header files

2.4. The constants section
2.5. The class section
2.6. The globals section
2.7. The function prototype section
2.8. The functions section
2.9. File Names

3. Functions

3.1. Header comment
3.2. Parameters

3.3. Function length

4. Variables

5. Constants

5.1. Constant definitions
5.2. Magic Numbers

6. Classes

7. Control flow

7.1. The if statement
7.2. The for statement

8. Lexical issues

8.1. Naming convention
8.2. Indentation and white space
8.3. Braces
8.4. Unstable layout
8.5. The preprocessor


1. Introduction

A style guide makes you a more productive programmer because it reduces gratuitous choice. If you don't have to make choices about trivial matters, you can spend your energy on the solution of real problems.

In these guidelines, a number of constructs are plainly outlawed. That doesn't mean that programmers using them are evil or incompetent. It does mean that the constructs are of marginal utility and can be expressed just as well or even better with other language constructs.

These guidelines are necessarily somewhat long and dull. They also mention features that you may not yet have seen in the class. Here are the most important highlights:

  1. Tabs every three spaces (8.2)
  2. Variable and function names are lowercase (8.1)
  3. Constant names are uppercase (8.1)
  4. Spaces after keywords and between binary operators (8.2)
  5. Braces must line up (8.3)
  6. No magic numbers (5.2)
  7. Every function must have a comment (3.1)
  8. At most 30 lines of code per function (3.3)
  9. At most 3 global variables per file (2.6)

2. Files or Modules

2.1. Source file layout

Each C++ program is a collection of one or more files or modules. The executable program is obtained by compiling and linking these files. Organize the material in each file as follows:

2.2. Header comment

Each module starts with a header comment block in the format below.

/*****************************************************************
PROJECT:        Program 1 
FILE:           wm.cpp 
PROGRAMMER:     Eugenia Fernandez  
DATE:   	6/11/99
PURPOSE:        widget manipulation 
*****************************************************************/ 

2.3. Included header files

This section lists all included header files.

/* INCLUDES ******************************************************/

#include <iostream> 
#include "CProduct.h" 

Do not embed absolute path names, e.g

#include "h:/efernand/cpt262/CProduct.h"  /* Don't !!! */ 

2.4. The constants section

This sections contains constants that are needed throughout the program file. Constants should appear in uppercase.

/* CONSTANTS ****************************************************/

const int GRID_SIZE = 20; 
const float CLOCK_RADIUS = 5; 

2.5. The class section

This section contains the definitions of classes. Prefix all class names with a C.

/* CLASSES ******************************************************/ 

class CProduct 
{ . . .

}; 

2.6. The globals section

This section contains the definitions of global variables.

/* GLOBALS ******************************************************/ 

float ANNUAL_RAISE;  /* this year's raise for all employees */

Every global variable must have a comment explaining its purpose.

Avoid global variables whenever possible. If you must, use at most three global variables in any one module. Prefix global variables with a g.

2.7. The function prototype section

This section lists all function prototypes of the module. Include pre and post conditions and any comments needed to understand the use of the function.   

/* FUNCTION PROTOTYPES ****************************************************/ 

long dat2jul(int d, int m, int y);
// Pre:  d within 1-31, m within 1-12, nonzero y
// Post: returns date in julian format

2.8. The functions section

This section starts with the main function and is followed by other function implementations.

/* MAIN ******************************************************************/ 
int main() 
{ . . . 

} 

/* Function Implementations **************************************/ 

long dat2jul(int d, int m, int y
// Pre:  valid calendar date with day (d) between 1-31, 
//          month (m) between 1-12 & nonzero year (y)
// Post: returns date in julian format
// Note: This algorithm is from Press et al., Numerical Recipes in C, 2nd ed., 
             Cambridge University Press 1992 
{ . . . 

} 

2.9. File Names

The class header file should carry the name of the class with a .h extension.  The file holding the class member functions should carry the name of the class with the .cpp extension.  For example, the class declaration for the CProduct class would be in the product.h file and its member function implementations would be in the product.cpp file. 

The file with the main function may be named main.cpp or named according to the assignment or lab, e.g., program1.cpp.

3. Functions

3.1. Header comment

Supply a comment of the following form for every function.

// Pre:  preconditions that must exist before function is called
// Post: explanation of return value or result of function
// Note: any additional notes needed to understand and use function  

Use the Note comment if the purpose of the function is not explained by the pre, post and function name and its pre- and post-condistions. Omit the Pre: comment if the function takes no parameters. 

long dat2jul(int d, int m, int y
// Pre:  valid calendar date with day (d) between 1-31, 
//          month (m) between 1-12 & nonzero year (y)
// Post: returns date in julian format
// Note: This algorithm is from Press et al., Numerical Recipes in C, 2nd ed., 
             Cambridge University Press 1992 
{ . . . 

} 

No function comment is required for the main procedure.

3.2. Parameters

Use meaningful parameter names.

Employee remove(int d, float s); /* huh? */ 

Employee remove(int department, float severance_pay); /* Ok */ 

Do not write void functions that return exactly one answer through a reference. Instead, make the result into a return value.

void find(vector<CEmployee> emplist, bool& found); /* Don't! */ 

bool find(vector<CEmployee> emplist); /* Ok */ 

Of course, if the function computes more than one value, some or all results can be returned through reference arguments.

3.3. Function length

Functions must have at most 30 lines of code. The function header, comments, blank lines and lines containing only braces are not included in this count. Functions that consist of one long if/else may be longer, provided each branch is 10 lines or less.

This rule forces you to break up complex computations into separate functions.

4. Variables

Do not define all variables at the beginning of a block. Define each variable just before it is used for the first time.

Every variable must be explicitly initialized when defined, or it must be set in the next statement (for example, through a >> instruction).

int pennies = 0; 

or

int pennies; 
cin >> pennies; 

Move variables to the innermost block in which they are needed.

while( /* ... */ ) 
{  float xnew = (xold + a / xold) / 2; 
   . . .
}

Do not define two variables on the same line
int dimes = 0, nickels = 0; /* Don't */ 

When defining a pointer variable, place the * with the type, not the variable.  Prefix all pointer variables with a p, e.g.

Link* p_link; /* Ok */ 

not

Link *p_link; /* Don't */ 

5. Constants

5.1. Constant definitions

In C++, do not use #define to define constants.

#define CLOCK_RADIUS 5 /* Don't */ 

Use const instead.

const float CLOCK_RADIUS = 5; /* the radius of the clock face */ 

5.2. Magic Numbers

A magic number is a integer constant embedded in code, without a constant definition. You may not use magic numbers in your code. Any number except 0, 1 and 2 is considered magic.

if (p.get_x() < 10) /* Don't */ 

Use const instead.

const float WINDOX_XMAX = 10; 

if (p.get_x() < WINDOW_XMAX) /* Ok */ 

Even the most reasonable cosmic constant is going to change one day. You think there are 365 days per year? Your customers on Mars are going to be pretty unhappy about your silly prejudice. Make a constant.

const int DAYS_PER_YEAR = 365; 

so you can easily cut a Martian version without trying to find all the 365's, 364's, 366's, 367's etc... in your code.

6. Classes

Prefix all class names with a C. Lay out the items of a class as follows:

class Class_name 
{
  public:
    constructors
    modifiers
    accessors

  private:
    data
    functions
};

All data fields of classes should be private.  Prefix all class data members with m_.  

Use Get and Set prefixes on accessor member functions,  e.g. GetX(), SetX().

Supply a default constructor for every class.

Place all class members of the same access type together, i.e. put all public members together. Avoid switching back and forth between private and public members.

7. Control flow

7.1. The if statement

Avoid the if...if...else trap. The code

if( ... ) 
      if( ... ) ...; 
   else 
   {  ...; 
      ...; 
   } 

will not do what the indentation level suggests, and it can take hours to find such a bug. Always use an extra pair of {...} when dealing with if...if...else:

if( ... ) 
{  if( ... ) ...; 
   else( ... ) ...; 
} /* {...} not necessary but they keep you out of trouble */ 


if( ... ) 
{  if( ... ) ...; 
} /* {...} are necessary */ 
else ...; 

7.2. The for statement

Only use for loops when a variable runs from somewhere to somewhere with some constant increment/decrement.

for (i = 0; i < a.size(); i++) 
   print(a[i]); 

Do not use the for loop for weird constructs such as

for (xnew = a / 2; count < ITERATIONS; cout << xnew) /* Don't */ 
{  xold = xnew; 
   xnew = xold + a / xold;
   count++;
}

Make such a loop into a while loop. That way, the sequence of instructions is much clearer.

xnew = a / 2; 
while (count < ITERATIONS) /* Ok */ 
{  xold = xnew; 
   xnew = xold + a / xold; 
   count++; 
   cout << xnew; 
} 

A for loop traversing a linked list can be neat and intuitive:

for (a.reset(); !a.at_end(); a.next()) 
   cout << a.current().get_name() << "\n"; 

8. Lexical issues

8.1. Naming convention

The following rules specify when to use upper and lowercase letters in identifier names.

  1. All variables are in lowercase (with an occasional under_score in the middle). For example, first_player.
  2. All data names in classes should be in lowercase (with an occasional under_score in the middle).  Prefix all member variables with m_.  For example, m_date.
  3. Function names can be in lowercase (with an occasional under_score in the middle) or with each word capitalized with no spaces.  For example, first_function or FirstFunction.  Pick one method and stick with it.  Do not mix them.
  4. All constants are in uppercase (maybe with an occasional UNDER_SCORE). For example, CLOCK_RADIUS.
  5. All class names start with uppercase and are followed by lowercase letters (maybe with an occasional UpperCase letter). Prefix all class names with a C. For example, CBankTeller.

Names must be reasonably long and descriptive. Use first_player instead of fp. No drppng f vwls. Local variables that are fairly routine can be short (ch, i) as long as they are really just boring holders for an input character, a loop counter etc... And, do not use ctr, c, cntr, cnt, c2 for five counter variables in your function. Surely these variables all have a specific purpose and can be named to remind the reader of it (e.g. ccurrent, cnext, cprevious, cnew, cresult...).

8.2. Indentation and white space

Use tab stops every 3 columns. That means you will need to change the tab stop setting in your editor!

Use blank lines freely to separate logically separate parts of a function.

Separate functions by comments like /***********/.

Use a blank space around every binary operator.

x1 = (-b - sqrt(b * b - 4 * a * c)) / (2 * a); /* Good */ 

x1=(-b-sqrt(b*b-4*a*c))/(2*a);/*Bad*/ 

Leave a blank space after (and not before) each comma, semicolon, and keyword, but not after a function name.

if (x == 0) 

f(a, b[i]); 

Every line must fit on 80 columns. If you must break a statement, add an indentation level for the continuation:

a[n] = .................................................. 

   + .................; 

If this happens in an if or while, be sure to brace in the next statement, even if there is only one.

if( ......................................................... 

      and .................. 

      or .......... ) 

{    ... 

}

If it wasn't for the braces, it would be hard to visually separate the continuation of the condition from the statement to be executed.

8.3. Braces

Opening and closing braces must line up, either horizontally or vertically.

while (i < n) { print(a[i]); i++; } 

or

while (i < n) 
{  print(a[i]); 
   i++; 
}

The trick is not to leave a blank line after the opening brace but to type a tab followed by the first line of the loop body.

Some programmers don't line up vertical braces but place the { behind the while.

while (i < n) {                   /* Don't */ 
   print(a[i]); 
   i++; 
}

But that makes it hard to check that the braces match. Other programmers place the { in a line all by itself.   The author thinks that isn't a good idea because it wastes a line of precious screen space, but it is acceptable to me.

while (i < n) 
{                                 /* Not recommended but acceptable */ 
   print(a[i]); 
   i++; 
}

8.4. Unstable layout

Some programmers take great pride in lining up the names of variables.

class CEmployee 
{ 
   private: 
      string	name;	
      int 	age;	
      float 	hourly_wage;	
      CTime 	hiredate;	
}; 

This is undeniably neat, and we recommend it if your editor does it for you. But don't do it manually. The layout is not stable under change. A data type that is longer than the preallotted number of columns requires that you move all entries around.

Some programmers like to format multiline comments so that every line starts with **.

/* This is a comment 

** that extends over 

** three source lines 

*/ 

Again, this is neat if your editor has a command to add and remove the asterisks. Otherwise, it is a silly thing to do since it is a powerful method of discouraging programmers from editing the comment. If you have to choose between pretty comments and comments that reflect the current facts of the program, facts win over beauty.

8.5. The preprocessor

It has been said that all usage of the preprocessor points to deficiencies in the programming language. C++ fixes some deficiencies. Do not use #define for constants or macros--use const and inline instead.

A legitimate use for the preprocessor is conditional compilation (e.g. #ifndef NDEBUG ... #endif).

To comment out a block of code that may itself contain comments, use #if 0 ... #endif. (Recall that C++ comments do not nest.)