Coding Standards
The purpose of a program is to tell another person what you want
the machine to do. - Donald Knuth
This document outlines standards for coding in C++. Small modifications may also
be used as a coding standard in Java. These standards fall into several
categories:
- File Structure
- Formatting Conventions
- Naming Conventions
- Scoping Conventions
- Usage Conventions
- Compile Errors and Warnings
- The file should conform to ISO standards for text files. In particular:
- The Windows carriage return character ^M should not appear in the file
- The file should end with a newline '\n'
- No tabs should be in the file
- No line should contain more than 80 characters
- The elements of a code file should appear in this order:
file header documentation
#include statements
constants
class declarations (if needed)
function prototypes (if needed)
class definitions (if needed)
implementations
- Files for code definitions and implementations should adhere to these
customs:
- For functions: prototypes should be placed in a file
with suffix ".h" (often called a header file), and
implementations in a corresponding file with suffix ".cpp" (often called
an implementation file):
xxx.h # contains function prototypes
xxx.cpp # contains implementations of functions prototyped in xxx.h
- For classes: class definitions should be placed in a file
with suffix ".h" (often called a header file), and
class member function implementations in a corresponding file with suffix ".cpp" (often called
an implementation file):
xxx.h # contains class definition and related function prototypes
xxx.cpp # contains implementations of functions and methods prototyped in xxx.h
- For templates (either function or class): class definitions and all
implementations should be placed in a header file
with suffix ".h":
xxx.h # contains class and function definitions and implementations
- A single header file may use the #include directive to make other
physical files logically part of the header file.
- Indentation should be set to two spaces
- Open brace { should appear on a line by itself, unless its matching
close brace is on the same line. (Handle other delimiter pairs the same way.)
- Close brace }, possibly followed by a semicolon };,
should appear on a line by itself, unless its matching open brace is on the
same line. (Handle other delimiter pairs the same way.)
- Open and close delimiter pairs (braces {}, parentheses (),
angle brackets <>) should be aligned vertically or horizontally:
void MyFunction { /* one line of code here */ } // OK
void MyFunction // OK
{
// code here
}
void MyFunction { // NOT OK
// code
}
- Function headers may be broken into "parameter
documentation" format:
void MyBigFunction
(
type1 parameter1, // documentation for parameter1
type2 parameter2, // documentation for parameter2
type3 parameter3, // documentation for parameter3
type4 parameter4 // documentation for parameter4
)
{
...
}
Note that the matching open and close parentheses are vertically aligned and
indented two spaces. This format is required for headers longer than 80 characters.
- Binary operators should be surrounded by clear space, except possibly
multiplication in arithmetic expressions:
x = a + b * c; // OK
x = a + b*c; // OK
x = a+b*c; // NOT OK
x=a+b*c; // NOT OK
x= a + b * c; // NOT OK
std::cout<<x; // NOT OK
std::cout << x; // OK
std::cin >>y; // NOT OK
- When declaring pointers, leave space between * and the variable:
int* iptr; // OK
int* iptr1, // OK
* iptr2; // OK
int * iptr; // OK
int * iptr1, // OK
* iptr2; // OK
int *iptr; // NOT OK
int *iptr1, // NOT OK
*iptr2; // NOT OK
- Compound names should use upper case letters to mark the beginning of the
next word likeThis and LikeThisToo
- Names of user-defined types (classes or enumerated types) should begin with upper case letters
LikeThis or This
- Names of functions, including class methods, should begin with upper case letters
LikeThis or This
- Names of variables should begin with a lower case letter
likeThis or this
- Names of class variables should end with the underscore character
likeThis_ or this_
- Names of types and functions should be chosen to be self-documenting
- Names of meaningful variables should be chosen to be self-documenting.
Names of variables whose function is internally important only,
such as loop counters, should be simple.
The following code for function Average illustrates these points:
float Average (int* a, size_t size)
{
if (size == 0) return 0;
float sum = 0;
for (size_t i = 0; i < size; ++i)
{
sum += a[i];
}
return sum / size;
}
Note that a is an array of integers (by declaration), size is
the size of the array, sum represents the sum of array elements, and
i is a loop counter. Both size and sum
have meaning beyond their type declaration and this meaning is captured in the
choices of names. On the other hand, a and i have no meaning
beyond what is given in their declarations.
- Resolve namespace scopes explicitly. Do not use general scope breakers such
as C++ using directives. For example:
using namespace std; // not OK
... // not OK
cout << "Hello World\n"; // not OK
std::cout << "Hello World\n"; // OK
The reason: The "using" directive effectively brings all of the namespace into
the global namespace, thus circumventing the reason we have namespaces in the
first place, which is to compartmentalize code. It is much clearer code to
resolve the scope of an object where and when it is needed.
-
Distinguish between meaningful variables and meaningless variables. For
example, in the function Average above,
size and sum
are meaningful, whereas a and i are meaningless (except for
type).
-
Meaningless variables such as loop counters should be given as small a
scope as possible by declaring them at the point of first use.
In particular, loop counters should have scope limited to the
loop itself, as illustrated above.
-
Meaningful variables should be declared at the beginning of the block within which
they are used, rather than at the point of first use. This convention serves to
clarify the meaningful computational elements in a block at the beginning, which
makes the code organization easier to understand.
- Every code file should have file header documentation containing at least
the following information: name of the file, date created, author/coder, and a
brief description of the file contents. A copyright statement may be used as
well, typically the last item in the header documentation. Here is one style for
header documentation:
/*
myfile.h
Chris Lacher
12/31/2008
version 2.0
Illustrates code file header documentation
*/
The following version applies tags for automated documentation systems:
/**
@file myfile.h
@author Chris Lacher
@date 2008-12-31
@version 2.0
Illustrates code file header documentation
*/
- Most class definitions are documented in the file header. However, in
situations where more than one class is defined in a file, each class should
have basic documentation sufficient to clarify the intended use of the
class. This may be part of the file header documentation or separate
documentation for each class in the file.
- To the extent that functions are not self-documenting, they too should have
documentation explaining the intended use and functionality captured by the
function.
-
Code detail documentation should be used sparingly. Generally, code should be
written to be self-documenting and formatted to be readable, so that no
code-level documentation is needed. In the rare cases where the code is so
intricate that it is difficult to follow it may be appropriate to supply the
reader with a summary of how the computation is being made. However, be wary to
keep the documentation up to date. It is quite common for documentation to
assert something is done while the code does something else entirely. Needless
to say, this is less helpful than simply having no documentation.
- Always protect header files from multiple read using the
#ifndef _FILENAME_H
#define _FILENAME_H
// code here
#endif
protocol.
- Always use angle brackets for include files:
#include <myfile.h> // OK - location of file is unspecified
#include "myfile.h" // NOT OK - location of file is hard coded (relative)
#include "/directory/myfile.h" // NOT OK - location of file is hard coded (absolute)
The reason: angle brackets allow for the included file to be movable without
editing the file in which they are included. Quotes force an edit of #include
statement whenever the relative locations of the includee and includor are
changed or the absolute path of the includee is changed. It is much better to
resolve these issues in the build record (makefile).
- A close brace that is separated from its matching open brace by more than 30
lines should be documented:
namespace xyz
{
...
switch(x)
{
...
} // switch(x)
...
} // namespace xyz
- Use prefix increment and decrement operators unless the postfix return value
is required:
x = y++; // OK - x has the value of y before y is incremented
x = ++y; // OK - x has the value of y after y is incremented
++y; // OK - no return value is created
y++; // NOT OK - return value is created and wasted
- Use declared constants rather than defined constants:
#define PI 3.14159 // NOT OK
const double PI = 3.14159; // OK
- Literals should not be embedded in code. Use constants to capture specific
numerical, character, and string literals.
-
Every (non-static) class variable must be initialized in an initialization list
in every class constructor. This applies even to variables that are
re-initialized in the constructor body. For example:
class IntArray
{
public:
IntArray (); // default constructor
IntArray (size_t); // 1-parameter constructor
IntArray (const IntArray&); // copy constructor
...
private:
size_t size_;
int * data_;
};
// default constructor
IntArray::IntArray () : size_(0), data_(0)
{}
// 1-parameter constructor
IntArray::IntArray (size_t size) : size_(size), data_(0)
{
data_ = new int [size_];
}
// copy constructor
IntArray::IntArray (IntArray& ia) : size_(ia.size_), data_(0)
{
data_ = new int [size_];
for (size_t i = 0; i < size_; ++i)
data_[i] = ia.data_[i];
}
- Code should compile without errors on Linux/Intel [linprog machines].
- With all warning flags set, code should compile without issued warnings on
Linux/Intel architecture [linprog machines].
- The alternative Unix/Sun architecture is a second and desireable place to
test code.
|