Department of Computer Science
©2002 All Rights Reserved
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.
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.
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.
class CardPile { |
CardPile::addToTop() { |
int CardPile::count = 52; |
int numberOfMarbles( 10 ); |
const int STATIC_OPTION( 25 ); |
const float CM_PER_INCH( 2.54 ); |
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.
void Counter::increment(){ |
Changes the state | OK |
int Counter::rounded(){ |
Returns a value | OK |
int Counter::incrementedValue(){ |
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.
Use names that make sense for your classes and class members. Dont 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.
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.
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.
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.
// 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
|
The declaration of every member function should be preceded in the header file by a block comment like this:
/* 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 wont know about them. * Post: describe any postconditions * that the function guarantees. * otherwise the client wont 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/>:
/** * 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 wont know about them. * @post describe any postconditions * that the function guarantees. * otherwise the client wont 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!
All C++ source files (both .cpp and .h) should begin with the following 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 :
/** * 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:
#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.
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).
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 its 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 readers
time without helping in any way.
In this section we describe how the source code should appear.
//
// 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
|
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.
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.
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 dont 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.
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.
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.
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. Dont 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.
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 editors 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.
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() )
);
|
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( "Im sorry, Dave, but "
"I'm afraid I cannot let you do that." );
}
|
CardPile::moveCard( int oldOrientation, Coordinates fromPile,
int newOrientation, Coordinates toPile ) {
|
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[];".
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.
struct is prohibited; use a class instead.
virtual in a base class, it should
also be declared virtual in every derived class.
throw
clause in the functions prototype.
{") 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.
/* ... */ C comments are acceptable for
header comments of classes and functions, but the // style should
be used for inline comments.
delete operation, the pointer variable must immediately
be assigned the null value (0), or some legal pointer value.
++ 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 cant 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.
&) to generate a pointer.
It leads to misunderstandings about how memory was allocated.
-> operator instead when possible.
bool type.
| 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. |
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.