Programming Style Standards for C++

Department of Computer Science
©2010 All Rights Reserved

Why a Style Standard?

Style standards and guidelines 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.

These standards exist to:

Although source code libraries follow different styles, that does not make conformance to this standard optional.

Consistency is an important goal. If you structure the code in multiple different ways, it will be harder for the reader to follow your code.

Note: these guidelines have evolved over the years, and the department has not updated all its material to comply with the latest version. You will see examples in class that, for reasons of emphasis or presentation, violate some of these rules. If you have any questions as to what exactly is correct, please talk to an instructor.

These standards strike a balance between discipline and flexibility. Forbidden things have been common sources of error, but special situations may arise in which a forbidden idiom seems to be the right thing. If you feel you have found such a situation, explain it to your instructor and ask permission to violate the standard for that specific case.

1. Files and File Names

C++ classes are written in two parts: the declaration and the definition. The declaration specifies the interface to a class, and the definition, or implementation, contains the bodies of the class members. The names of both of these files must begin with the class name. Files containing declarations are usually called header files) and end with the .h suffix. Files containing definitions end with the .cpp suffix.

Files not containing classes, such as a main program file, cannot follow this naming rule because they have no class name.

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 data or values that never change, should have 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. (You should make appropriate use of named constants in your code, in strong preference to literal values.)

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 lower mixed case ). As an alternative, you can use separating underscores.


Class Names

UpperMixedCase

Constant Names

UPPER_CASE

Class Field Names

lowerMixedCase

Local Names

lower_case or lowerMixedCase

Function Names

lowerMixedCase

Figure 1: Case Rules for Naming

3. Routine Roles

Every function should either:

This approach reduces chances of misunderstandings.

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

Not Recommended

Figure 2: Examples of Proper and Improper Function Roles

Due to the historical inheritance from the C language, there are violations of this convention in the Standard Library. Discuss this with your instructor if you believe you have a situation that warrants a violation.

4. Function and Data Names: Grammatical Choices

Names of objects and their classes should be noun phrases, and names of functions should be verb phrases. Do not use cute names, or names that describe something amusing or significant only to you; this will not help anyone follow your code.

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.

bool large();

adjective

bool saturated();

past participle; part of the verb phrase 'is saturated'

bool isParent();

is followed by noun

bool is_empty();

to remove ambiguity -- "empty" is also a verb

Figure 3: Naming Predicates

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

When classes become larger than 7 or 8 methods, organizing members of a class into groups makes it easier for the reader to find something (things are organized into logical sections).

Use the access rights keywords to organize various members of a class. Access rights are one of many ways to distinguish groups of members, and the language syntax already provides a separator!

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

The basic C++ comment forms include: plain inline, plain block, javadoc block and triple-slash inline. The first two forms are for file comment blocks and internal comments, while the second two forms are for computer-generated code documentation.

Inline comments begin with two slashes and continue to the end of the line. These are useful for presenting short text about a particular line of code. For multiple lines using an inline comment, you must begin each line with //. For longer text, you may want to use block comments instead.

Block comments are one or more lines of comments that should appear on lines by themselves without any code. Block comments begin with /* and end with */. The block should include some white space to set it off from the surrounding code.

Javadoc comments begin with /** and end with */. Source code processing tools, such as Doxygen (Doxygen.org), read these comments and convert them into online documentation. Like javadoc, doxygen uses all the same tags, such as @param. Another name for javadoc comment blocks is a docucomment.

Triple-slash comments begin each line with /// and continue to the end of the current line. This is the C# language docucomment commenting standard. Doxygen can read these comments and convert them into online documentation.

The comments in Figure 4 below show a content example of file comment block text for a .h header file showing RCS keywords with their filled-in values. (Note: This code is not fully conformant to these guidelines.) The #include guards are required; the function groupings are optional.

// $Id: progstyle-cpp-2009.html,v 1.1 2011/03/25 13:46:32 cs4 Exp cs4 $
//
// Revisions:
// $Log: progstyle-cpp-2009.html,v $
// Revision 1.1  2011/03/25 13:46:32  cs4
// Initial revision
//
// 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 {

/// Counter maintains an integer whose value
///      can be changed by +1 or -1.
/// @author: Max Headroom
/// @author:  Bruce Banner

class Counter {

public: // Accessors

        /// @return current value of the count
        int getCount();

public: // Mutators

        /// Increment the counter
        void increment();

        /// @param value to set 
        void setCount( int value );

public: // State testing

        /// @return non-zero iff the counter is zero, and zero otherwise
        int isZero();

        /// @return non-zero iff the counter < 0, and zero otherwise
        int isNegative();

private: // Data members

        /// The counter itself
        int count;

}; // Counter

}
#endif

Figure 4: Example of Header File Structure with function groups and #ifdef guards

 

6.1. Member Function Documentation Comments

The declaration of every member function must be preceded in the header file by a documentation comment in javadoc or triple-slash form.

/**
 * <DESCRIPTION>
 *
 * @param <PARAM1 description, if any>
 * @return <return description, if not void>
 * @pre <precondition description>
 * @post <postcondition description>
 * @throws <describe exceptions that may occur>
 */
int getCount() const ;

Figure 5a: Javadoc standard Function Header Comments

Alternatively, you may follow triple-slash, C#-style conventions.

///
/// <DESCRIPTION>
///
/// @param <PARAM1 description, if any>
/// @return <return description, if not void>
/// @pre <precondition description>
/// @post <postcondition description>
/// @throws <describe exceptions that may occur>
///
int getCount() const ;

Figure 5b: Javadoc-Style Function Header Comments

The description is always required unless the @param and @return text is abundantly clear and further description would be redundant. The description must describe only those modifications and state changes that a client can see. The description must not describe parts of the implementation that are should be hidden.

The arguments, returns, precondition, postcondition, and exceptions sections must be omitted if not relevant to the function. Another synonym tag for @throws is @exception.

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, but the documentation tool may need to be configured to process the shared documentation properly.

In the implementation (.cpp) file, a one-line, 'brief' description must precede each function definition. The header file is the place for the complete documentation; do not duplicate the header file's function documentation.

Do not repeat information available from the declaration itself. For example, return an instance of class A for a constructor would serve no useful purpose since the header file and the primary purpose of the constructor both provide that information.

Note on Descriptions: Routines do something; indicate this by using the imperative mood in the comment. State what the routine does with its arguments and describe any side effects produced.

The private members require commenting in addition to public and protected members. People with legitimate access to the source code deserve docucomments to understand private components.

6.2. Class Documentation Comments

After the file's documentation comment block and necessary #includes and namespace declarations, all C++ class header files (.h) should contain the following class documentation comments before the class declaration:

///
/// a general description of the class's role
///
/// @author principal author name
/// @author contributor name (what contributed)
///

Figure 6a: Standard Class Header Comments

You may alternatively follow javadoc-style conventions :

/**
 * a general description of the class's role
 *
 * @author  principal author name
 * @author  contributor name (what contributed)
 */

Figure 6b: Javadoc-Style Class Header Comments

 

The first author is the principal author, and the remaining ones are contributors. For this course, the contributor is your teammate; in general, contributors are those people who contributed intellectual content to the development of the particular source unit. After each contributor's name provide a one-line description of their contribution in parentheses. You should not include your instructor or teaching assistants, but anyone else must be listed by name.

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 general in your description of the entire class.

To ensure that the compiler does not double-include any header file, enclose the content of header files in preprocessor directives as shown in the figure below:

#ifndef NAMESPACE_CLASS_H
#define NAMESPACE_CLASS_H

...

declarations

...

#endif

Figure 7: Protecting Header Files from Being Included More than Once

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

6.3. End-of-class, End-of-namespace, End-of-function Syntactic Marker Comments

When a block of statements becomes long enough that it is difficult to find the start or end of the block when looking at the opposite end, it is time to comment the end of block marker (}).

Mark the closing brace at the end of a namespace, class declaration or function definition with an inline comment repeating its name.

Similar comments at the closing brace of if, loops, and switch statements are optional; use them when appropriate.

6.4. Comments for 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. Long routines should be broken into logical sections with comments describing in general terms the work to be done in each section.

Extremely long routines may indicate bad design. Long routines should be factored 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 time and are no real help.

7. Spatial Organization

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. Be careful: incorrect spacing can confuse the reader by suggesting relationships that are not correct.

7.2. Vertical Spacing

Add blank lines to improve readability. For example, you should:

  1. Break routines into logical sections with blank lines or comments;

  2. Separate local variable definitions from the rest of a function's code;

  3. Separate functions from their neighbors with a blank line or two;

  4. Separate class fields from their neighbors with a blank line.

However, do not put blank lines into the program at random. You must recognize the tradeoff between spacing things out and allowing the reader to see an entire component without having to scan too many pages.

7.3. Indentation Levels

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 special indentation case is namespaces. Do not indent the body of a namespace. This is a pragmatic consideration to avoid 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. Comments have been omitted to save space. Figures 8 and 9 show how to lay out a member function. The function signature begins at the left margin. Indent the body of the function from the signature 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 (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.

The closing brace lines up with the if keyword to which it belongs. (Some other styles may be acceptable, as long as you are consistent; ask your instructor).

/// standardDeviation
float Grades::standardDeviation() {

        float mean; // The mean of the data
        float deviation; // Sum of squares of dev.
        int count; // Loop counter

        // compute the mean.

        mean = 0;
        for ( count = 0; 
              count < numGrades; 
              count += 1 ) {
                mean += grade[ count ];
        }
        mean /= numGrades;

        // compute sum of squares of 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;
        }

        // compute and return standard deviation.

        return sqrt( deviation / ( numGrades - 1 ) );

} // standardDeviation

Figure 8: Code layout example

 

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 9: Indentation in C++: The if Statement

 

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.

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

Figure 10: Indentation in C++: The switch Statement

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

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. Do not change the tab stops because it changes only how the information is displayed, not how it is stored. When your instructor examines or prints your code, the lines may be display/print too long, and you will lose points.

7.4. Horizontal Spacing

Horizontal spacing, the use of spaces within code and comments, is a great help for readability, especially when followed consistently. When using markers (parentheses, square brackets, braces, or angle brackets) around argument lists, leave no space before the left marker, one space after it, and one space before the right marker.

Put a space after every comma and never before a comma. Parentheses within expressions should have a space both before and after. Binary operators should have a space on each side, and no space ever separates a unary operator from its operand.

The compiler will be confused if template specifications are nested and consecutive > and > symbols are not separated with spaces.

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

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

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 11: Splitting Up Long Expressions

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 12: Large Boolean Expression in an if Statement; Long String Constants

CardPile::moveCard( int oldOrientation, 
                    Coordinates fromPile,
                    int newOrientation, 
                    Coordinates toPile ) {
...

Figure 13: Alternatives for Functions with Long Formal Parameter Lists

7.5 Declarations

Only one declaration should appear on a line.

It is preferable to 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[];". This example reads "sectors is an array of pointers to int"; the pointer operator refers more closely to the name. (The * is part of the declarator.)

8. Special C++ Requirements

C++ is a multiparadigm language; it lets the programmer create software constructs that are not considered object-oriented. In addition, because of its downward compatibility with the original C language, one can do many low-level things. To impose discipline and achieve higher level of quality than would otherwise occur, the following additional rules apply to programs written in C++:

  1. Global variables and functions (that is, variables and functions that are neither members nor friends of some class) are prohibited. However, this does not include operator<<() and operator>>() overloadings, which are almost all global, and the main() function.

  2. Use of namespaces, while not required, is strongly encouraged to avoid name collisions.

  3. If a function is declared virtual in a base class, also declare it virtual in every derived class to restate its virtual nature.

  4. All data members declared in a class will be private.

  5. Declarations of member functions in header files will include parameter names to assist the reader.

  6. Include files must not contain non-templated executable code. Inline functions are permitted if they are mutators or accessors implemented with a single statement.

  7. No file may contain code for more than one class other than those classes whose definitions are nested.

  8. The member groups and members in the source file will appear in the same order that they are shown in the header file.

  9. 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.

  10. All initialization will be done using the constructor form rather than the assignment form.

  11. Initialization of member data in class constructors will be done with the subobject constructor form rather than with assignment statements. This is illustrated in Figure 14.

  12. Old-style /* ... */ C comments are acceptable for header comments of classes and functions, but the // style should be used for inline comments.

  13. Braces always enclose if, while, do, and for nested statements, even if they only enclose a single statement.

  14. 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.

  15. After a delete operation, the pointer variable must immediately be assigned the null value (0), or some other legal pointer value.

  16. 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 cannot be avoided, such as using iterators from our standard libraries. Prefix ++ and -- are encouraged because they are more efficient than the postfix form.

  17. Do not use pointers more than necessary. Here are some examples.

  18. Do not use integer or pointer values as Booleans; always use the built-in bool type.

  19. 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.)

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

Reason

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;

}

Initializes fields twice.

Figure 14: Proper Initialization

9. Nothing is Cast in Stone

You will by default lose points for not following these standards, but that does not have to be the case. If you can make a good, strong argument to your instructor explaining how and why you would like to stray from the standards, you are welcome to do so. Be prepared to demonstrate, by comparison, why a different approach would yield a more understandable and/or less error-prone piece of code. And apply the variation consistently.


bks. Tue Mar 30 18:25:54 EDT 2010