C++ Coding Standards

A piece of code is only written once, but it has to be read and understood many more times. It's always tempting to take shortcuts to try to write the code faster, but that will mean slowing down other developers (or the same developer in the future) who look at the code. Having some general coding guidelines can help significantly toward that goal. However, it's important to remember they are just guidelines and they may be broken when it makes sense to do so.


Files

Avoid excessively long files

Files with more than a couple of hundred lines become combersome to work with. If a file gets to be several thoundands of lines long, it's a good sign that it could benefit from some refactoring.

Avoid exessively wide files

Limit lines of code to less than 80 characters. Very long lines are harder to read, as the eyes have to travel a longer distance from the end of one line to the beginning of the next, making it easier to lose track of what the next line is. Shorter lines also print better; paper is not infinately wide like a window. Also, 80 columns is still the default terminal width in many (if not all) terminal programs.

Define one class per file

The only exception should be small classes that are very tightly coupled with a larger one defined in the same file. It makes it easier to determine where to go to look at a class' interface or implementation.

Name files after the class they define

If a file contains the declaration for the class FooBar, it should be called FooBar.hh, not foobar.hh, Foo_bar.hh, etc.

Put a class' interface in a .hh file and its implementation in a .cc file

Code should not go in a header file. The only exceptions should be templatized functions and short inline functions. Having too much code in the header file makes it harder for clients of the class to just look at the interface.

Preprocessor

Use include guards in header files.

It is usually not safe for a header file to be included multiple times. The contents should be with wrapped by #ifdef/#endif directives:

#ifndef FOOBAR_HH
#define FOOBAR_HH

// ... FooBar.hh contents ...

#endif

Avoid #define for constants

Use const or enum declations instead. They are much safer because they can be type-checked by the compiler, plus they are more useful when debugging because the names are included in the symbol table.

Avoid #define for macro functions

Use inline functions instead, for the same reasons mentioned about constants. Since they are inlined, they also avoid the function call overhead like the macros do.

Include headers in alphabetical order

An individual header is easier to find (e.g. when checking if it needs to be included or if it already is) among many #includes if they are listed in some sort of order.

Space

White space is your friend

Inserting white space does not increase compile time, code size, run time, etc. Make liberal use of it to make code more readable.

Use spaces instead of tabs for indentation

Programmers have differing ideas of what the indentation should be (2, 3, and 4 spaces are all common) and usually configure their editor to indent that many spaces per tab character. Then when another programmer with different tab width settings opens the document the code aligment is wrong. It is best to avoid the problem entirely by indenting with spaces (ASCII 32) instead of tabs (ASCII 9). Most editors can be configured to automatically expand tabs to spaces.

Group related lines of code with white space

Just like paragraphs group related sentences in prose, lines of code that are logically related should also be grouped together using blank lines as separators.

Use spaces around symbols

It just makes them easier to parse visually if there is a space around +, -, *, /, %, =, etc.

Don't insert space between a function name and the opening parenthesis

Keep the argument list tightly bound the the function it belongs to.

Insert space between a C/C++ statement and the opening parenthesis

Statements that require parenthesis (if, for, while) are not function calls. They shouldn't look like they are.

Insert space between items in a list

Lists (function arguments, array initializers, etc) should have space between items. For example:

var1 = Function(arg1, arg2, arg3);

int days_in_month[] = { 31, 30, 28, ... };

Names

Use names for numbers

There should be (almost) no "magic numbers" in the code. The only exceptions should be 0 and 1. Sometimes a magic number is okay if its meaning is obvious in its context and limited in scope.

Use meaningful names

A developer be able to get some idea of what a variable is used for from just its name without having to dig through the rest of the code to see how or where it is created or used.

Avoid ambiguous abbreviations

While many abbreviations can be clear (e.g. "num" will almost always be short for "number") others are more likely to be interpreted in more than one way. For example, "def" could be short for either "definition" or "default" and both seem just as likely. The name no_retries could be interpreted as "number of retries" or "don't allow retries."

Uppercase only first letter of acronyms

When using acronyms in mixed-case names, use lower case for all but the first letter. The name StartHttpRequest is easier to parse visually than StartHTTPRequest, even though it could be argued that the second one is more correct.

Use lower case and underscores for variable names

For example, acct_number, status_code, etc.

Use mixed case for aggregate types (classes, structs, etc)

Capitalize the first letter of ever word. Examples: ThreadPool, IdpRequest, ReplyBlockHeader, etc.

Use mixed case for function names

Using mixed case helps visually differentiate function names from variable names.

Use a leading underscore for member variable names

It makes it easy to diffentiate them from local variables.

Use upper case and underscores for preprocessor macros only

It makes them easier to identify when they must appear in the code, which shouldn't be too often anyway since the program will be using const and inline functions instead.

Use g and mixed case for global variables

Global variables should be discouraged in general, but if necessary they should be identified as such. Examples: gAccountId, gNumThreads, gDebugLevel.

Use k and mixed case for global constants

Examples: kMaxRetries, kTimeout, kPortNumber

Error Handling

Use exceptions for logic errors

Nobody checks return codes. Exceptions force developers to either handle the error or acknowledge the fact that they chose to ignore it and was not just an oversight.

Note that exceptions should only be used for errors in program logic. That is, errors that are triggered because the programmer made a mistake (e.g. wrong argument to an API call), not for errors where the user made a mistake. Those errors are much more likely to happen in normal usage and should not impose a performance penalty.

Use assert in production code

Assert checks are often removed from final builds. Leaving the assert in provides a clue as to why a program failed and can greatly speed up finding and fixing bugs.

Non-recoverable errors should exit the program

As mentioned before, nobody checks return codes. And nobody checks error logs either. Having the program exit is the most likely way to get the error noticed so it gets fixed. It is usually a bad a idea to allow the program to appear as though everything is working fine when it is really not.

Write useful error messages

Errors like "not found" or "cfg is null" are not nearly as useful as "file foo/bar.cfg not found" or "no config file specified".

Comments

Write comments for non-trivial functions, classes, etc

Every non-trivial class, function, etc, should have some information on what it is, how it's used, etc even if it is a single sentence. Ideally, they should be in a format that allows for easy automatic extraction by tools like Doxygen.

Use comments to explain non-obvious code

If you had to think a lot about how to write a particular piece of code, chances are it will not be obvious to other developers looking at it either.

Don't comment out large blocks of code

It's hard enough to understand a piece of code without being confused by pieces that are never executed. Editors that support syntax highlighting can help by making it easier to visually identify and ignore comments, but that shouldn't be relied on. And editors aren't smart enough (as far as I know) to ignore commented code when searching for variable names, functions, etc.

If the code really isn't needed, remove it entirely. It can be retrieved later from a previous version of the source file if necessary. (I mean, you are using some sort of revision control system, right?)

Other

Resist premature optimization

As engineers, we all like to be clever and figure out a way to accomplish something using one less byte, or one less instruction, often increasing complexity as a result. But unless it is known that the code is in a time-critical section of the program, it is seldom worth making a piece of code harder to understand, test, or debug in order to save a few microseconds.

One statement per line

Avoid putting multiple staments in a single line. It makes code harder to parse visually. This rule applies to if and while statements too. That is, avoid code like if (foo) { blah blah blah } even if it fits in a single line.

This is especially important for statements that affect the code path, like return, break, continue, etc. Not seeing them when scanning the code can lead the developer to follow code that may not even be executed.

Avoid unecessary dependencies

Dependencies make it harder to unit test code because they increase the amount of external code that needs to be compiled and/or linked. Many dependencies are unavoidable, but the ones that could be eliminated should be.

Pass non-built in types by constant reference

Passing them by value would require calling the object's copy constructor, which is an unnecessary performance penalty. Passing them as a const & avoids making the copy.

Pass all arguments that will be modified by pointer

Passing them by non-constant reference works also, but it is harder to see that the argument will be modified. It may be obvious when looking at the function's interface, but it's not obvious in the code that's actually calling the function.

Do not inline long functions.

The peformance boost gained by avoiding the function call becomes negligible compared to the time spent excuting a long function body, so it's not worth cluttering the interface.


Last updated March 17, 2007 by Javier Alvarado.