In general, every .cpp
file should have an associated .h
file. There are some common
exceptions, such as unittests and
small .cpp
files containing just a
main()
function.
Correct use of header files can make a huge difference to the readability, size and performance of your code.
if (a < b) { cout << "less"; cin >> i; }another acceptable styles is:
if (a < b) { cout << "less"; cin >> i; }
You must choose 1 style for braces and use it consistently.
total = total + 1;
int sum(int x, int y) {...}
foo[x]
instead of foo [x]
, and int min(int x, int y)
instead of int min (int x, int y)
if (x == 100) {...}
instead of if(x == 100) {...}
Function names, variable names, and filenames should be descriptive; eschew abbreviation.
Give as descriptive a name as possible, within reason. Do not worry about saving horizontal space as it is far more important to make your code immediately understandable by a new reader. Do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word.
Good variable names:int price_count_reader; // No abbreviation. int num_errors; // "num" is a widespread convention. int num_dns_connections; // Most people know what "DNS" stands for.
int n; // Meaningless. int nerr; // Ambiguous abbreviation. int n_comp_conns; // Ambiguous abbreviation. int wgc_connections; // Only your group knows what this stands for. int pc_reader; // Lots of things can be abbreviated "pc". int cstmr_id; // Deletes internal letters.
i, j, k
)p1, p2
, etc. int a[CLASS_SIZE];
const double FUDGE_FACTOR = 27.82;
const int CLASS_SIZE = 27;
total_due
totalDue
double capitalGainsDue()
Declarations
double salesTax; // calculated sales tax int length, // length of rectangle in inches width; // width of rectangle in inches const double TAX_RATE = 0.0825, // current tax rate INFLATION_RATE = 0.025; // current rate of inflation
Note: If your variable names are descriptive (and they better be), you may not need a comment after each one.
Note: I prefer one declaration per line with the type, but will allow variables to be declared as shown above.
Constants
const double SALES_TAX_RATE = 0.0825; // current sales tax rate
tax = subtotal * SALES_TAX_RATE;
Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.
C++ allows you to declare variables anywhere in a function. We encourage you to declare them in as local a scope as possible, and as close to the first use as possible. This makes it easier for the reader to find the declaration and see what type the variable is and what it was initialized to. In particular, initialization should be used instead of declaration and assignment, e.g.:
int i; i = f(); // Bad -- initialization separate from declaration.
int j = g(); // Good -- declaration has initialization.
vector<int> v; v.push_back(1); // Prefer initializing using brace initialization. v.push_back(2);
vector<int> v = {1, 2}; // Good -- v starts initialized.
Variables needed for if
, while
and for
statements should normally be declared
within those statements, so that such variables are confined
to those scopes. E.g.:
while (const char* p = strchr(str, '/')) str = p + 1;
There is one caveat: if the variable is an object, its constructor is invoked every time it enters scope and is created, and its destructor is invoked every time it goes out of scope.
// Inefficient implementation: for (int i = 0; i < 1000000; ++i) { Foo f; // My ctor and dtor get called 1000000 times each. f.DoSomething(i); }
It may be more efficient to declare such a variable used in a loop outside that loop:
Foo f; // My ctor and dtor get called once each. for (int i = 0; i < 1000000; ++i) { f.DoSomething(i); }
Use a struct
only for passive objects that
carry data; everything else is a class
.
structs
should be used for passive
objects that carry data, and may have associated
constants, but lack any functionality other than
access/setting the data members. The accessing/setting of
fields is done by directly accessing the fields rather
than through method invocations.
If more functionality is required, a
class
is more appropriate. If in doubt, make
it a class
.
const
Use const
whenever it makes sense.
Definition: Declared variables and parameters can be preceded
by the keyword const
to indicate the variables
are not changed (e.g., const int foo
). Class
functions can have the const
qualifier to
indicate the function does not change the state of the
class member variables (e.g., class Foo { int
Bar(char c) const; };
).
Decision: const
variables, data members, methods
and arguments add a level of compile-time type checking;
it is better to detect errors as soon as possible.
Therefore we strongly recommend that you use
const
whenever it makes sense to do so:
const
.const
whenever
possible. Accessors should almost always be
const
. Other methods should be const if
they do not modify any data members, do not call any
non-const
methods, and do not return a
non-const
pointer or
non-const
reference to a data member.const
whenever they do not need to be modified after
construction.Though a pain to write, comments are absolutely vital to keeping our code readable. The following rules describe what you should comment and where. But remember: while comments are very important, the best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments.
When writing your comments, write for your audience: the next contributor who will need to understand your code. Be generous — the next one may be you!
Use either the //
or /* */
syntax, as long as you are consistent.
You can use either the //
or the /*
*/
syntax; however, //
is
much more common. Be consistent with how you
comment and what style you use where.
At the top of each source code file, you will place a block of comments listing at least the name of the file, the name of the programmer, the course and section number, and a brief description of the program's purpose.
Generally a .h
file will describe the
classes that are declared in the file with an overview of
what they are for and how they are used. A
.cpp
file should contain more information
about implementation details or discussions of tricky
algorithms. If you feel the implementation details or a
discussion of the algorithms would be useful for someone
reading the .h
, feel free to put it there
instead, but mention in the .cpp
that the
documentation is in the .h
file.
Do not duplicate comments in both the .h
and the .cpp
. Duplicated comments
diverge.
Every class definition should have an accompanying comment that describes what it is for and how it should be used.
// Iterates over the contents of a GargantuanTable. class GargantuanTableIterator { ... };
If you have already described a class in detail in the comments at the top of your file feel free to simply state "See comment at top of file for a complete description", but be sure to have some sort of comment.
Declaration comments describe use of the function; comments at the definition of a function describe operation.
Every function declaration should have comments immediately preceding it that describe what the function does and how to use it. These comments should be descriptive ("Opens the file") rather than imperative ("Open the file"); the comment describes the function, it does not tell the function what to do. In general, these comments do not describe how the function performs its task. Instead, that should be left to comments in the function definition.
Types of things to mention in comments at the function declaration:
Here is an example:
// Calculates the cubic value of a real number. // parameter: value the input value. // returns: the parameter raised to the 3rd power double cube(double value);If more complex functions may require more detailed comments, e.g. we may need to specify the preconditions and the postconditions of a function.
// Searches through an array of strings for a given name. // Precondition: the input string array must be sorted alphabetically. // Postcondition: the position of the name in the array (or -1) has been returned. // parameter: students, the input array of names. // parameter: name, the name to look for. // returns: the position of the array where the name was found, or -1 if the name // was not found in the array. int findName(const String [] students, const String name);
However, do not be unnecessarily verbose or state the completely obvious. Notice below that it is not necessary to say "returns false otherwise" because this is implied.
// Returns true if the table cannot hold any more entries. bool IsTableFull();
When commenting constructors and destructors, remember that the person reading your code knows what constructors and destructors are for, so comments that just say something like "destroys this object" are not useful. Document what constructors do with their arguments (for example, if they take ownership of pointers), and what cleanup the destructor does. If this is trivial, just skip the comment. It is quite common for destructors not to have a header comment.
If there is anything tricky about how a function does its job, the function definition should have an explanatory comment. For example, in the definition comment you might describe any coding tricks you use, give an overview of the steps you go through, or explain why you chose to implement the function in the way you did rather than using a viable alternative.
Note you should not just repeat the comments
given with the function declaration, in the
.h
file or wherever. It's okay to
recapitulate briefly what the function does, but the
focus of the comments should be on how it does it.
findName
declared above, we may want
to specify the search algorithm that the function uses, e.g.
// Uses binary search to look for a name in the given alphabetically sorted // string array. For more information about binary search see: // https://en.wikipedia.org/wiki/Binary_search_algorithm int findName(const String [] students, const String name) { ... }
In general the actual name of the variable should be descriptive enough to give a good idea of what the variable is used for. In certain cases, more comments are required.
Each class data member (also called an instance variable or member variable) should have a comment describing what it is used for. If the variable can take sentinel values with special meanings, such as a null pointer or -1, document this. For example:
private: // Keeps track of the total number of entries in the table. // Used to ensure we do not go over the limit. -1 means // that we don't yet know how many entries the table has. int num_total_entries;
As with data members, all global variables should have a comment describing what they are and what they are used for. For example:
// The total number of tests cases that we run through in this regression test. const int kNumTestCases = 6;
In your implementation you should have comments in tricky, non-obvious, interesting, or important parts of your code.
Tricky or complicated code blocks should have comments before them. Example:
// Divide result by two, taking into account that x // contains the carry from the add. for (int i = 0; i < result->size(); i++) { x = (x << 8) + (*result)[i]; (*result)[i] = x >> 1; x &= 1; }
Also, lines that are non-obvious should get a comment at the end of the line. These end-of-line comments should be separated from the code by 2 spaces. Example:
DoSomething(); // Comment here so the comments line up. DoSomethingElseThatIsLonger(); // Two spaces between the code and the comment.
Note that if you have several comments on subsequent lines, it can often be more readable to line them up:
When you pass in a null pointer, boolean, or literal integer values to functions, you should consider adding a comment about what they are, or make your code self-documenting by using constants. For example, compare:
bool success = CalculateSomething(interesting_value, 10, false, NULL); // What are these arguments??
versus:
bool success = CalculateSomething(interesting_value, 10, // Default base value. false, // Not the first time we're calling this. NULL); // No callback.
Or alternatively, constants or self-describing variables:
const int kDefaultBaseValue = 10; const bool kFirstTimeCalling = false; Callback *null_callback = NULL; bool success = CalculateSomething(interesting_value, kDefaultBaseValue, kFirstTimeCalling, null_callback);
Note that you should never describe the code itself. Assume that the person reading the code knows C++ better than you do, even though he or she does not know what you are trying to do:
// Now go through the b array and make sure that if i occurs, // the next element is i+1. ... // Geez. What a useless comment.