Programming Style Standards for C++

Department of Computer Science
©2002 All Rights Reserved

Why a Style Standard?

Part of the purpose of style guidelines is to enhance the readability of programs by allowing all the members of a team to know how to interpret things like indentation and choice of identifiers. But C++ more than many others calls for standards for another reason. It is a loose, undisciplined language; it allows you to do many things which run counter to modern software engineering principles. We further restrict the way you can program in C++ in order to

There are many well-written C++ programs that use other styles. But in any reasonably well-run development team, you will find rules that encourage consistency in the code developed by different developers. In other words, the fact that there are so many different style standards does not detract from the arguments supporting their use.

Be aware, however, that many libraries (including the ones we have) make use of techniques that differ from what we teach in our courses. This does not render the style standards unnecessary: on the contrary, they are even more important. The fact that a feature or technique is legal, or that some existing library does it, is no excuse for being sloppy when writing your own code. When developing your own personal style, consistency is an important goal. If you constantly do things differently, it will be harder for the reader to follow your code.

You should also know that these guidelines have evolved over the years, and the department has not always had the time to update all its source code files to be compliant with the latest version. You will also see examples in class that, for reasons of emphasis or presentation, violate some of our own rules. If you have any questions as to what exactly is correct, please talk to an instructor.

Finally, these standards attempt to strike a balance between discipline and flexibility. Things that are forbidden have been shown to be common sources of error. However, special situations may arise in which a forbidden idiom seems to be just what is needed. If you feel you have found such a situation, explain it to your instructor, who may give you permission to violate the standard for that specific case.

1. Files and File Names

C++ classes are written in two parts: the declaration, which specifies the interface to the class, and the definition or implementation, which contains the bodies of the routines used by the class. The names of both of these files must begin with the class name; names of files containing declarations (often called header files) end with .h and names of files containing definitions end with .cpp.

2. Identifiers: Word Separation and Case

Class names should begin with a capital letter; the remaining letters should be lower case. The second and subsequent words of multi-word class names are also capitalized, but not separated by underscores.

Constant data, i.e. predefined values or data values that never change, should be given names to indicate their use and meaning. These names should be all upper case letters. The words of multi-word identifiers are separated from one another by underscores.

Names for other data and functions should be all lower case letters. The words of multi-word identifiers are distinguished from one another by capitalizing the first letter of each one after the first ( bumpy names ) or alternatively, by use of separating underscores.

Figure 1: Examples of Proper Use of Letter Case in Identifier Names
class CardPile {
 :
 :
};
CardPile::addToTop() {
 :
 :
}
int CardPile::count = 52;
int numberOfMarbles( 10 );
const int STATIC_OPTION( 25 );
const float CM_PER_INCH( 2.54 );

3. Routine Roles

When you see a function call that is returning a value, it is often difficult to guess whether or not that function has other so-called side effects due to its execution. The convention that each function you write either:

but not both should normally be followed because it reduces the chances of misunderstandings.

Figure 2: Examples of Proper and Improper Function Roles
void Counter::increment(){
 count += 1;
}
Changes the state OK
int Counter::rounded(){
 return ( count / Base ) * Base;
}
Returns a value OK
int Counter::incrementedValue(){
 count += 1;
 return count;
}
Changes the state
Returns a value
NO GOOD

There are more violations of this rule in the Standard Library than one could count, due to historical precedence from the C language and libraries. There are often reasonable exceptions you may make to the rule. In general, the case is when the function is basically a procedure, but must return a small modicum of information such as status. Discuss this with your instructor if you are not sure what to do.

4. Function and Data Names: Grammatical Choices

Use names that make sense for your classes and class members. Don’t use cute names, or names that describe something amusing or that has significance only to you; this will not help anyone (not even you) follow your code. In general, names of objects and their classes should be nouns, and names of functions should be verbs.

Data member names, and names of member functions that return non-Boolean results should, in general be nouns. Boolean member functions are sometimes called predicates; use adjectives, or the prefix is followed by a noun, as names for them. In case of ambiguity, it may be permissible to use is followed by an adjective as the name for a Boolean function.

Figure 3: Naming Predicates
bool large(); adjective
bool saturated(); past participle, but nevertheless a noun modifier
bool isParent(); is followed by noun
bool is_empty(); to remove ambiguity -- "empty" is also a verb

The convention for mutator routines that set the value of a quantity managed by the class is to name it set followed by the name of the quantity. The convention for accessor routines that return such a value is to name it get followed by the name of the quantity.

5. Organizing Member Groups

It is useful to split the features (members) of a class into smaller groups for several reasons:

Here is what you should do. Choose some classification criteria for the members of each class (each class may have its own criteria), and group the members into physical sections according to those criteria. In C++, each member group should begin with one of the keywords public:, protected:, or private: followed by a short comment describing the distinguishing characteristics of the members in the group that follows. In general, public groups should be shown first, then protected and private groups. Figure 4 illustrates these points.

6. Comments

A good commenting style strategy is just as important as the style of the code itself.

Inline comments are those which appear on the same line as the thing that they describe. These comments are most useful for presenting small bits of specific information about a particular line of code. If what you want to say is longer than the space remaining on the line, then block comments should be used instead.

Block comments are one or more lines of comments that appear on lines by themselves, i.e., without any code. Precede the first line with /* (or perhaps /**), have each subsequent line in the block begin with *, and follow the last line with a line containing only */. Of course, if you are using //, each line must begin with //. The block should include some white space that sets it off from the surrounding code, thus making it easier to find.

Please note that the comments in Figure 4 below are not complete according to the guidelines that appear later in this document.

Figure 4: Examples of Proper Naming of Functions Based on Their Roles
// File: $Id: progstyle-c++.html,v 1.4 2003/12/01 23:08:03 cs4 Exp $
// Author: Max Headroom
// Contributors: Bruce Banner, Tony Stark
// Description: Maintain an integer whose value
// 	can be changed by +1 or -1.
// Revisions:
// $Log: progstyle-c++.html,v $
// Revision 1.4  2003/12/01 23:08:03  cs4
// Changed '.C' references to '.cpp'.
//
// Revision 1.3  2002/09/18 02:49:20  cs4
// Responded to suggestions from CS4 faculty.
//
// Revision 1.2  2002/09/17 18:40:12  cs4
// First complete rework into html (jeh)
//

#ifndef KITCHEN_COUNTER_H
#define KITCHEN_COUNTER_H

namespace Kitchen {

class Counter {

public: // Access
	// Get the current value of the count
	int getCount();

public: // Modification
	// Increment the counter
	void increment();
	// Set the counter to a specific value
	void setCount( int value );

public: // State testing
	// Is the counter zero?
	int isZero();
	// Is the counter negative?
	int isNegative();

private: // Data members
	// The counter itself
	int count;
}; // Counter

}
#endif

6.1. Member Functions

The declaration of every member function should be preceded in the header file by a block comment like this:

Figure 5a: Standard Function Header Comments
/* Name: function-name
 *
 * Description:	describe what the function does,
 *	including any I/O it performs.
 * Arguments:	describe what each argument is; do not simply
 *	repeat their types from the argument list.
 * Modifies:	description of any changes the function makes
 *	to the externally visible state of the object.
 * Returns:	description of what the function returns.
 * Pre:		describe any preconditions
 *	that should be observed when the function is called.
 *	otherwise the client won’t know about them.
 * Post:	describe any postconditions
 *	that the function guarantees.
 *	otherwise the client won’t know about them.
 * Exceptions:	describe any exceptions that may be thrown.
 */

Alternatively, you may follow javadoc-style conventions. There is a tool you can use to generate the HTML pages from it . Its documentation can be found at <http://www.cs.rit.edu/~cs4/pub/OnlineDoc/UserGuide/>:

Figure 5b: Javadoc-Style Function Header Comments
/**
 * Describe what the function does, including
 *	any I/O it performs.
 *
 * @param	parameter-name	description
 * :
 * :
 * @modifies	any state modified by this function
 * @return	description of what the function returns.
 * @pre		describe any preconditions
 *	that should be observed when the function is called.
 *	otherwise the client won’t know about them.
 * @post	describe any postconditions
 *	that the function guarantees.
 *	otherwise the client won’t know about them.
 * @exception	exception-name	circumstances
 * :
 * :
 */

Be careful that the Modifies clause only describes state changes that the client can see; do not describe changes to parts of the implementation that are supposed to be hidden. The Arguments, Modifies, Returns, Assertions (pre,post), and Exceptions sections may be omitted if the function takes no arguments, does not modify anything, returns no value, has no assertions, or throws no exceptions, respectively. However, (Name and) Description are always required. Keep in mind that there are four different sections that could potentially say the same thing: Description, Modifies, Post, and Returns. Don't be needlessly redundant. Postconditions may include mention of object state that does not change, or of “old” values of parameters or object state from when the function was first invoked. Occasionally, similar functions can be grouped together under one block of header comments if their descriptions are sufficiently similar. For example, accessor functions can sometimes be done this way.

In the implementation (.cpp) file, the definitions of functions should be preceded by a comment that only states the name of the function. The idea is to have the reader always refer to the header file for documentation. A duplicate of the header file comment block may also be included, but now you have to remember to keep both copies up to date.

Do not repeat information that is available from the declaration statement itself, e.g., “return an instance of class A.” Such comments serve no useful purpose. Routines do something; indicate this by using the imperative mood in the comment. It may be thought that private members require less or no commenting, but this is a bad idea. People who have legitimate access to your source code (maybe you, when you come back months later to modify your own code) deserve all the help they can get to understand it!

6.2. Classes

All C++ source files (both .cpp and .h) should begin with the following comments:

Figure 6a: Standard Class Header Comments
// File: $Id: progstyle-c++.html,v 1.4 2003/12/01 23:08:03 cs4 Exp $
// Author: author name
// Contributors: other people who contributed to the class
// Description: a description of what the class does
// Revisions:
// $Log: progstyle-c++.html,v $
// Revision 1.4  2003/12/01 23:08:03  cs4
// Changed '.C' references to '.cpp'.
//
// Revision 1.3  2002/09/18 02:49:20  cs4
// Responded to suggestions from CS4 faculty.
//
// Revision 1.2  2002/09/17 18:40:12  cs4
// First complete rework into html (jeh)
//

Again, you may alternatively follow javadoc-style conventions :

Figure 6b: Javadoc-Style Class Header Comments
/**
 * a description of what the class's role is, in general
 *
 * @version	$Id$
 * @author	author name
 * @author	contributor's name (mark with nature of contribution)
 *
 * Revisions:
 *	$Log$
 */

The contributors should be the list of your teammates, and/or other people who contributed intellectual content to the development of this class; you do not need to include your instructor or the teaching assistants, but anyone else must be listed by name, along with an idea of what their contributions were, in parentheses immediately following their names.

The description should be several lines and give an overview of the class. Try not to repeat information that is going to appear in the comments for each individual class feature; instead, be more unifying and general in your description of the entire class. The RCS keywords are shown here as you enter them; when the file is checked out, RCS will expand these keywords to include their values.

To simplify specifying which header files should be included in a source file, the content of header files will be enclosed in directives as shown in the figure below:

Figure 7: Protecting Header Files from Being Included More than Once
#ifndef NAMESPACE_CLASS_H
#define NAMESPACE_CLASS_H

:
:
declarations
:
:

#endif

where NAMESPACE is replaced with the capitalized namespace for the class, and CLASS is replaced with the capitalized class name. Examples of all of this were shown in Figure 4.

6.3. Syntactic Markers

The closing brace after a class declaration or function definition must be followed by an inline comment repeating its name. Similar comments at the closing brace of if, looping, and switch statements are optional; use them if the block of statements enclosed is long enough so that it is difficult to find the start of it when looking at the end (see §7.2 Indentation, below).

6.4. Clarification of Detail

Comments are needed in the body of a routine if the logic of the code is not obvious. For example, if you are doing an obscure calculation, explain what it’s doing and why. Because lines of code are all different lengths, inline comments are harder to locate and therefore less useful for this; use block comments instead. Somewhat long routines should be broken into logical sections with block comments describing in general terms the work to be done in each section. Truly long routines are an indication of bad design; they should be broken into smaller subroutines.

Comments that do not contain enough information to help someone else to figure out what is going on, such as “Calculate the magic number” are worse than useless; they waste the reader’s time without helping in any way.

7. Spatial Organization

In this section we describe how the source code should appear.

Figure 8: Code layout example
//
// standardDeviation
//
float Grades::standardDeviation() {
	float mean; // The mean of the data
	float deviation; // Sum of squares of dev. from mean
	int count; // Loop counter

	//
	// First, compute the mean.
	//
	mean = 0;
	for( count = 0; count < numGrades; count += 1 ) {
		mean += grade[ count ];
	}
	mean /= numGrades;

	//
	// Now compute the sum of the squares of the differences
	// between each grade and the mean.
	//
	deviation = 0;
	for( count = 0; count < numGrades; count += 1 ) {
		int dev; // Deviation of one grade.
		dev = grade[ count ] - mean;
		dev *= dev;
		deviation += dev;
	}

	//
	// Finally, compute and return the standard deviation.
	//
	return sqrt( deviation / ( numGrades - 1 ) );

} // standardDeviation

7.1. Layout

C++ programs contain many different entities nested within one another. In the case of declarations, the nesting determines which parts are subordinate to other parts; in the case of statements, it determines the way in which they are executed. Anything that can be done to make these relationships more clear to the reader is therefore extremely valuable. The judicious use of white space in your source files can make your program easier to read and understand. But be careful: incorrect spacing can confuse the reader by suggesting relationships that are in fact not correct.

7.2. Vertical Spacing

Blank lines may be added as needed to improve readability. For example, long routines are broken into logical sections with blank lines and block comments, a blank line separates local variable declarations in a function from other statements, and member groups and function definitions are separated with blank lines. However, do not put blank lines into the program at random. You also have to recognize a tradeoff between spacing things out more and allowing the reader to see an entire component without having to scan too much or turn pages.

7.3. Indentation

Each time text is indented, it should be moved to the right by the same number of spaces. A minimum of 2 and a maximum of 8, or one tab stop, is recommended. Tabs are useful rather than spaces because it is much easier to get things lined up correctly using them in some editors (but see §7.3.1).

The rule for indenting things nested within other things is simple: The different parts of one thing are lined up with one another, but nested things are indented one level more than the thing that encloses them.

The one exception is namespaces. You don’t need to indent inside a namespace. This is a pragmatic consideration to keep you from having to indent almost every line in a file!

The previous examples have shown how class declarations should be indented; the following figures illustrate various statements. In some cases the comments have been omitted to save space. Figures 8 and 9 show how a member function is written. The prototype begins at the left margin. The body of the function is indented from this by one level, clearly showing both that these statements are part of the routine and where the routine ends. This also shows how if statements are indented: the else keyword is part of the if statement and is therefore aligned with it (you may put all 3 tokens on one line, i.e., "} else {"), but the enclosed statements are indented to show the nesting. If one of the nested statements is another if statement, the statements it encloses are indented another level. Loops are indented in a similar manner.

We normally line up the closing brace with the if keyword with which it belongs. Some other styles are acceptable, as long as you are consistent.

Figure 9: Indentation in C++: The if Statement
int ShopControl::minuteProcessing( int time ) {
	iterations += 1;
	if ( time >= BEGIN_DAY_CYCLE && time <= END_DAY_CYCLE ) {
		timeclock.advance();
		if ( state != DAY_CYCLE ) {
			state = DAY_CYCLE;
			lighting.startDayCycle();
			securitySystem.startDayCycle();
		}
	}
	else {
		if ( state != NIGHT_CYCLE ) {
			state = NIGHT_CYCLE;
			lighting.startNightCycle();
			securitySystem.startNightCycle();
		}
		else {
			lighting.randomOnOff();
			securitySystem.takePictures();
		}
	}
} // minuteProcessing

Figure 10 shows how a switch statement is indented. The case labels are part of the switch itself, and are therefore aligned with it. The other enclosed statements are indented one level to show the nesting.

Figure 10: Indentation in C++: The switch Statement
int Calculator::applyOperator( int oper, int arg1, int arg2 ){
	int result;
	switch ( oper ) {
	case OP_ADD:
		result = arg1 + arg2;
		break;
	case OP_SUB:
		result = arg1 - arg2;
		break;
	case OP_MULT:
		result = arg1 * arg2;
		break;
	case OP_DIV:
		result = arg1 / arg2;
		break;
	default:
		cerr << "Invalid operator: " << oper << endl;
		result = 0;
		break;
	} // switch
	return result;
} // applyOperator

To improve the readability of template classes, the template keyword and parameterized type names should appear on a line by themselves.

7.3.1. Tabs and Editors

On most UNIX® systems, tab stops are eight spaces. This is the spacing used for indenting in these standards, so there is no reason to change to anything different. Many editors allow you to change the tab stops. Don’t do this, because it only changes how the information is displayed, not how it is stored. When your code in displayed in any other way, such as when your instructor looks at it, the lines may be too long, and you will lose points.

7.4. Horizontal Spacing

When using parentheses, square brackets, braces, or angle brackets to delimit argument lists, we recommend no space before the left marker, one space after it, and one space before the right marker. Commas should always be followed by a space, but never preceded by one. Parentheses in expressions should have a space both before and after. Binary operators should also be surrounded by a space on each side, but no space ever separates a unary operator from its operand. Although the spacing tips mentioned here are a preference and not a rule, be consistent, and remember that the compiler can be confused if template specifications are nested and the sequential < and > symbols are not separated with spaces.

The maximum line width should be 80 characters, including white space at the beginning of the line. To make this rule easier to follow, always set your text editor’s line length to 80 and run it inside a window of that same width.

If you need to type in something that belongs on one line according to the earlier rules, but exceeds 80 characters, then you must find some syntactically sensible points at which to break the line into shorter segments. Succeeding parts of the line should be indented from the first by less than a normal indenting level. Some possibilities are shown in the figures that follow.

Figure 11: Splitting Up Long Expressions
valid = ( n_first.rankIncrement().rank() == n_second.rank() ) &&
        ( n_first.color() != n_second.color() );
legalMove =
	(
		f_pile.isEmpty() &&
		(
			!t_pile.isEmpty() &&
			equalRank( t_pile.top, firstFoundationCard )
		)
	)
	||
	(
		!f_pile.isEmpty() &&
		!t_pile.isEmpty() &&
		sequentialCards( f_pile.top(), t_pile.top() )
	);

Figure 12: Large Boolean Expression in an if Statement; Long String Constants
if (
	(
		foundationPileNum % 2 == 1 &&
		rules.sequentialCards( dp.top, cardToMove )
	)
	||
	(
		foundationPileNum % 2 == 0 &&
		rules.sequentialCards( cardToMove, dp.top )
	)
) {
	dp.addToTop( cardToMove );
	sp.removeFromTop();
	table.message( "OK" );
}
else {
	voiceSynthesizer.say( "I’m sorry, Dave, but "
	           "I'm afraid I cannot let you do that." );
}

Figure 13: Alternatives for Functions with Long Formal Parameter Lists
CardPile::moveCard( int oldOrientation, Coordinates fromPile,
                    int newOrientation, Coordinates toPile ) {

CardPile::moveCard( int oldOrientation, // original orientation of the card Coordinates fromPile, // pile we’re taking the card from int newOrientation, // new orientation for the card Coordinates toPile // pile we’re moving the card to ) {

7.5 Declarations

Normally, only one declaration should appear on a line. It is OK to violate this for groups of variables that basically all serve the same purpose. Examples would be coordinates, array indices (unless the different dimensions have markedly different meanings), and child pointers in a binary tree node.

Attach any operators to the variable to which they apply. For example, to declare variable sectors to be of type int*[], the declaration should be "int *sectors[];".

8. Special C++ Requirements

C++ is a multiparadigm language in that it lets the programmer create software constructs that would not be considered object-oriented. In addition, because of its downward compatibility with the original C language, one can do many things that could diminish the resulting quality of the program. One of the purposes of standards is to impose discipline in order to achieve better communication and a higher level of quality than would otherwise occur. An additional purpose of this standard is to prevent students from doing work that, no matter how acceptable in general, is clearly outside the realm of what our courses intend to teach. Therefore, the following additional rules apply to programs written in C++, unless otherwise stated for a specific assignment.

  1. Global variables and functions (that is, variables and functions that are neither members nor friends of some class) are prohibited, the obvious exception being the main function.
  2. Use of namespaces, while not required, is strongly encouraged to avoid name collisions.
  3. The use of struct is prohibited; use a class instead.
  4. Private and protected inheritance are not allowed.
  5. If a function is declared virtual in a base class, it should also be declared virtual in every derived class.
  6. All data members declared in a class will be private.
  7. Declarations of member functions in header files will include parameter names to assist the reader.
  8. All exceptions thrown by a function must be listed in the throw clause in the function’s prototype.
  9. Include files may not contain executable code. However, inline functions are OK if they are mutators or accessors implemented with a single statement.
  10. All of the code to implement a class will be in the same source file, except in the case of large classes, where each member group may be put into a separate source file. No file may contain code for more than one class; a possible exception to this is classes whose definitions are nested.
  11. The member groups and members in the source file will appear in the same order that they are shown in the header file.
  12. Variable declarations should be placed either at the start of a block (after the "{") or at the beginning of the group of lines of code that use the variables. If a declaration shows up somewhere other than the beginning of a block, it must be preceded by a blank line.
  13. All initialization will be done using the constructor form rather than the assignment form. In addition, initialization of member data in class constructors will, whenever possible, be done with the constructor form rather than with assignment statements. This is illustrated in Figure 14.
  14. Old-style /* ... */ C comments are acceptable for header comments of classes and functions, but the // style should be used for inline comments.
  15. Braces are always used around nested statements, even if they only enclose a single statement.
  16. There are several statements in C++ that can alter the normal flow of execution through a program's statements. They are return, continue, break, and goto.
  17. After a delete operation, the pointer variable must immediately be assigned the null value (0), or some legal pointer value.
  18. Do not use ++ and -- as a part of some larger expression. Their typical use is to increment or decrement a counter. There are also some places where they can’t be avoided, such as using iterators from our standard libraries. Postfix ++ and -- are discouraged, simply because they are less efficient than the prefix form in many cases.
  19. Do not use pointers more than necessary. Here are some examples.
    1. Do not use the address operator (&) to generate a pointer. It leads to misunderstandings about how memory was allocated.
    2. When you do need a pointer, avoid dereferencing it. Use the -> operator instead when possible.
    3. If you can use a reference variable instead (such as when dynamic memory allocation is not needed), do so.
  20. Do not use integer or pointer values as Booleans; always use the built-in bool type.
  21. In variable declarations, the preferred initialization is to use constructor parentheses wherever possible, instead of the equals sign. (You have no choice but to use an equals sign when initializing static constants of a primitive type or when using an array initializer.)

Figure 14: Proper Initialization
Preferred Misleading use of "=" Reason
int x( 5 );
int x = 5;
An equals sign in a declaration
invokes a constructor rather
than the assignment operator.
string s( "Hello" );
string s = "Hello";
Foo *pf( new Foo( 4 ) );
Foo *pf = new Foo( 4 );
Right Wrong
Point p( 3, 9 );
Point p = Point( 3, 9 );
This creates two points and
performs a copy operation
Point::Point( int x, int y ):
    x_coord( x ), y_coord( y ) {
}
Point::Point( int x, int y ) {
	x_coord = x;
	y_coord = y;
}
Formal arguments get
initialized twice.

9. Nothing is Cast in Stone

Remember that these are guidelines. Although you will bye default lose points for not following them, that does not have to be the case. If you can make a strong argument to your instructor as to why you would like to stray from the guidelines, you are encouraged to do so. Just be prepared to demonstrate, by comparison, why a different approach would yield a more understandable and/or less error-prone piece of code.