Dr. Brian's C++ Coding Standard

Clean & Simple Code

Which option are you?

// Option 1:
const char* HIGHEST_PRIORITY = "clean code";
cout << HIGHEST_PRIORITY << endl;

// Option 2:
for(char*m="\0rd0ct33lri";cout<<(char)*(m--+10),*(m+10););

It is always best to write simple, clear code that is easy to understand, debug and maintain.

Use Meaningful Names

All identifiers must have meaningful, human-readable, English names. Avoid cryptic abbreviations such as "dspl()", "cntStd()", or "stdRegYYYYMMDD". Instead use:

void display();
int countStudents();
char dateStudentRegistered[30];

Exception 1: loop variables may be i, j or k:

for (int i = 0; i < 10; i++) {
    ...
}

Exception 2: variables with very limited scope (<20 lines) may be shortened if the purpose of the variable is clear.

void swapMoney(float *pMoney1, float *pMoney2) 
{
    float tmp = *pMoney1;
    *pMoney1 = *pMoney2;
    *pMoney2 = tmp;
}

Naming Conventions

Constants must be all upper case, with multiple words separated by '_':

const int DAYS_PER_WEEK = 7;

Functions must start with a lower case letter, and use camelCase. Functions should be named in terms of an action:

double calculateTax();
bool verifyInput();

Global constants are acceptable, and should be placed in the most restrictive scope possible. For example, if it is used in only one .cpp file, then define the constant inside that file. If a constant is needed in multiple files, place it in a .h file (and make it static).

Global variables must be avoided as much as possible. When absolutely required, their names should be prefixed with g_:

int g_bad;
double g_doNotUseGlobals;

Boolean (bool) variables should be named so that they make sense in an if statement:

if (isOpen) {
    ...
}
while (!isEndOfFile && hasMoreData()) {
    ...
}

Pointers should be prefixed with 'p' in order to remind the programmer that it is a pointer.

int *pNumStudents;

Constants should be preferred over macros (#define). Macros must be all upper case, with words separated by '_':

const int MONTHS_PER_YEAR = 12 // Better than a #define!
#define MONTHS_PER_YEAR 12     // OK, but not great.

Use named constants instead of literal numbers (magic numbers). It is often acceptable to use 0 and 1; however, it must be clear what they mean:

// OK:
int i = 0;
i = i + 1;

// Bad: What are 0 and 1 for?!?
someFunction(x, 0, 1);

Indentation and Braces {...}

There's always time for perfect indentation.

Tab size is 4; indentation size is 4. Do not replace tabs with spaces. Use tabs at the start of a line to indent code to the current level of indentation. Use spaces to indent code beyond the current level of indentation. This format should make the code readable in all editors, but you will have to configure your editor to maintain this formatting.

if (j < 10) {
→   counter = getStudentCount(lowIndex,
→   ··························highIndex);
→   if (x == 0) {
→   →   if (y != 0) {
→   →   →   x = y;··········// Insightful comment here
→   →   }
→   }
}

Braces follow the K&R style. Opening brace is at the end of the enclosing statement; closing brace is on its own line, lined up with the start of the opening enclosing statement. Statements inside the block are indented one tab.

for (int i = 0; i < 10; i++) {
→   ...
}

while (i > 0) {
→   ...
}

do {
→   ...
} while (x > 1);

if (y > 500) {
→   ...
} else if (y == 0) {
→   ...
} else {
→   ...
}

Exception 1 (as per K&R): Functions have their opening braces on their own line:

void foo()
{
→   ...
}

"Heretic people all over the world have claimed that this inconsistency is ... well ... inconsistent, but all right-thinking people know that (a) K&R are _right_ and (b) K&R are right.", Linux kernel coding style.

Exception 2: If statements with multi-line conditions have the starting brace aligned on the left to make it easier to spot the block in the if statement.

if (someBigBooleanExpression
····&& !someOtherExpression)
{
→   ...
}

All if statements and loops should include braces around their statements, even if there is only one statement in the body:

if (a < 1) {
    a = 1;
} else {
    a *= 2;
}
while (count > 0) {
    count--;
}

Statements and Spacing

Declare each variable in its own definition, rather than together ("int i, j"). This is because pointer declarations can be confusing:
"int* p1, p2;" makes a pointer and an int.

int *p1;
int p2;

Each statement should be on its own line:

// Good:
i = j + k;
l = m * 2;

// Bad (what are you hiding?):
if (i == j) l = m * 2;
    cout << "Can ya read this?\n";

All binary (2 argument) operators (arithmetic, bitwise and assignment) and ternary conditionals (?:) must be surrounded by one space. Commas must have one space after them and none before. Unary operators (!, *, &, - (ex: -1), + (ex: +1), ++, --) have no additional space on either side of the operator.

i = 2 + (j * 2) + -1 + k++;
if (i == 0 || j < 0 || !k) {
    arr[i] = i;
}

Add extra brackets in complex expressions, even if operator precedence will do what you want. The extra brackets increase readability and reduce errors.

if ((!isReady && isBooting)
    || (x > 10)
    || (y == 0 && z < (x + 1)))
{
    ...
}

However, it is often better to simplify complex expressions by breaking them into multiple sub-expressions that are easier to understand and maintain:

bool isFinishedBooting = (isReady || !isBooting);
bool hasTimedOut = (x > 10);
bool isOldFirmware = (y == 0 && z < (x + 1));
if (!isFinishedBooting
    || hasTimedOut
    || isOldFirmware) 
{
    ...	
}

Comments

Comments which are on one line should use the // style. Comments which are or a couple lines may use either the //, or /* ... */ style. Comments which are many lines long should use /* ... */.

Each module (.h file) must have a descriptive comment in the .h file describing the general purpose of the module. Recommended format is shown below:

/*
 * The student module processes the information about a 
 * university student. Data includes student number, 
 * name, and address. 
 */
 ...

Each function listed in a .h file may have a comment describing what it does. However, the name of the function, combined with an understanding of the module, should be enough to tell the user what it does.

Comments should almost always be the line before what they describe, and be placed at the same level of indentation as the code. Only very short comments may appear inline with the code:

// Display final confirmation message box.
callSomeFunction(
        0,              // Parent.
        "My App",       // Title
        "test");        // Message

Comments vs Functions

Your code should not need many comments. Generally, before writing a comment, consider how you can refactor your code to remove the need to "freshen" it up with a comment. Often, comments are used as follows:

int main()
{
    // Open the file:
    // (NOTE: not actually working C code!)
    int fileHandle = open("test.txt");
    if (fileHandle < 0) {
        printf("Error opening file.\n");
        exit(EXIT_FAILURE);
    }
    
    // Write to file:
    printToFile("Testing with\n");
    printToFile("many lines of data.\n");
    
    // Close file:
    close(fileHandle);
    
    return EXIT_SUCCESS;
}

You should re-write this using quite short (3-5 line) functions (functions not shown here):

int main()
{
    int file = openInputFile();
    writeToFile(file);
    closeFile(file);
    
    return EXIT_SUCCESS;
}

When you do write a comment, it must describe why something is done, not what it does:

// Cast to unsigned char to avoid runtime error for 
// international characters when compiling with MS 
// Visual Studio.
if (isAlpha((unsigned char)ch)) {
    ...
}

Files

Header files have the extension .h; source files have the extension .cpp. Treat file names as being case sensitive: if the file is named Car.h, use #include "Car.h", not #include "car.h".

All header files must use include guards of the form:

#ifndef FILENAME_H
#define FILENAME_H
...
#endif

Header files must be self-contained. For example, if it uses strings, then "#include <string.h>" in the .h file. The client code must be able to include the .h file without having to first include other .h files.

Place all #include directives at the top of the file, instead of distributing them throughout your code.

Function prototypes must include full parameter names instead of (the valid, but poor style) "void printResult(int, char*);". The parameter names in the prototype must match the function definition (for style, not for compiler).

void printResult(int numStudents, float avgShoeSize);

Order your files so that the most general function is at the top, and the sub-routines are below it. For example, your main file should have main() at the top, and then the functions which it calls below it. This will require prototypes, but allows the reader to read your code top-down.

Other

Either post-increment or pre-increment may be used on its own:

i++;
++j;

Avoid using goto. If possible, design your loops to not require the use of "break" or "continue".

All switch statements should include a "default" label. If the "default" case seems impossible, place an "assert(false);" in it. Comment any intentional fall-through's in switch statements:

switch(buttonChoice) {
case YES:
    // Fall through
case OK:
    printf("It's all good.\n");
    break;
case CANCEL:
    printf("It's over!\n");
    break;
default:
    assert(false);
}

Use plenty of assertions. Any time you can use an assertion to check that some condition is true which "must" be true, you can catch a bug early in development. It is especially useful to verify function pre-conditions for input arguments or the object's state.

Never let an assert have a side effect such as "assert(i++ > 1);". This may do what you expect during debugging, but when you build a release version, the asserts get compiled out (they are macros). Therefore, the i++ won't happen in the release build.

References:

  1. Kernighan and Ritchie's "The C Programming Language", 1988.
  2. Linux kernel coding style.