Style Guide¶
Committing yourself to writing tidy, well-structured code from the start sets you up for good times to come. Your code will be easier to test, will have fewer bugs, and what bugs there are will be more isolated and easier to track down. You finish faster, your results are better, and your life is more pleasant. What’s not to like?
The guidelines below identify some of style qualities we will be looking for when grading your programs. As with any complex activity, there is no one “best” style, nor a definitive checklist that covers every situation. That said, there are better and worse choices and we want to guide you toward the better choices.
In grading, we will expect that you make a concerted effort to follow these practices. While it is possible to write code that violates these guidelines and yet still exhibits good style, we recommend that you adopt our habits for practical purposes of grading. If you have theoretical points of disagreement, come hash that out with us in person. In most professional work environments you are expected to follow that company’s style standards. Learning to carefully obey a style guide, and writing code with a group of other developers where the style is consistent among them, are valuable job skills.
Layout, Indentation, and Whitespace¶
Indentation: Consistent use of whitespace/indentation always! Proper whitespace/indentation illuminates the structure of your program and makes it easier to follow the code and find errors.
Increment indent level on each opening brace
{, and decrement on each closing brace}.Chose an increment of 2-4 spaces per indent level. Be consistent.
Do not place more than one statement on the same line.
// confusing, hard to follow while (x < y) { if (x != 0) { binky(x); } else { winky(y); y--; }} return x;
// indentation follows structure while (x < y) { if (x != 0) { binky(x); } else { winky(y); y--; } } return x;
Long lines: When any line is longer than 100 characters, break it into two lines. Indent the overflow text to align with text above.
result = longFunctionName(argument, 106 * expression * variable) + variable - longerFunctionName() + otherFunction(variable);
result = longFunctionName(argument, 106 * expression * variable) + variable - longerFunctionName() + otherFunction(variable);
Blank lines: Use blank lines to separate functions and logical groups of statements within a function.
Whitespace: Add space between operators and their operands. Add parentheses to show grouping where precedence might be unclear to reader.
int root = (-b+sqrt(b*b-4*a*c))/2*a;
int root = (-b + sqrt((b * b) - (4 * a * c))) / (2 * a);
Names¶
Choose meaningful identifiers. This reduce the cognitive load for reader and self-documents the purpose of each variable and function.
Nouns for variable names: For variables, the question is “What is it?” Use a noun (
name,scores) add modifier to clarify (courseName,maxScore). Do not repeat the variable type in its name (nottitleString, justtitle). Avoid one-letter names likeaorp(exceptions for loop countersi,jor, coordinatesxandy). Never name a variablel, much too easily confused with the number one.Verbs for function names: For functions, the question is “What does it do?” Functions which perform actions are best identified by verbs (
findSmallest,stripPunctuation,drawTriangle). Functions used primarily for their return value are named according to property being returned (isPrime,getAge).Use named constants: Avoid sprinkling magic numbers throughout your code. Instead declare a named
constvalue and use where that value is needed. This aids readability and gives one place to edit value when needed.const int VOTING_AGE = 18;
Capitalization: Use camel-case for names of functions and variables (
countPixels), capitalize names of classes/types (GridLocation), and uppercase names of constants (MAX_WIDTH). Conventions allows reader to quickly determine which category a given identifier belongs to.
Variable scope¶
Scope: Declare variables in the narrowest possible scope. For example, if a variable is used only inside a loop, declare it inside the scope for the loop body rather than at the top of the function or at the top of the file.
Don’t reuse same name in inner scope: Declaring a variable in inner scope with same name as a variable in outer scope will cause inner use of name to “shadow” the outer definition. Not only is this confusing, it often leads to difficult bugs.
No global variables: Do not declare variables at global scope. When there is information to be shared across function calls, it should flow into and out via parameters and return values, not reach out and access global state.
Use of C++ language features¶
Stanford library structures over STL structures: You should be using the data structures and other libraries included in the Stanford library instead of the ones included in the C++ standard template library (i.e. use
Vectorinstead ofvector)Prefer C++ idioms over C idioms: Since C++ is based on C, there is often a “C++ way” to do a given task and also a “C way”. For example, the “C++ way” to print output is via the output stream
cout`, while the “C way” is usingprintf. C++ strings use thestringclass, older code uses the C-stylechar*. Prefer the modern C++ way.// old school char* str = "My message"; printf("%s\n", str);
// modern and hip string str = "My message"; cout << str << endl;
for vs while: Use a
forloop when the number of repetitions is known (definite); use awhileloop when the number of repetitions is unknown (indefinite).// loop exactly n times for (int i = 0; i < n; i++) { ... } // loop until there are no more lines string str; while (input >> str) { ... }
break and continue in loops: Wherever possible, a loop should be structured in the ordinary way with clear loop start, stop, advance and no disruptive loop control. That said, there are limited uses of
breakthat are okay, such as loop-and-a-half (while(true)withbreak) or need to exit loop mid-iteration. Use ofcontinueis quite rare and often confusing to reader, better to avoid.Use of fallthrough in switch cases: A switch case should almost always end with a
breakorreturnthat prevents continuing into the subsequent case. In the very rare case that you intend to fallthrough, add a comment to make that clear. Accidental fallthrough is the source of many a difficult bug.switch (val) { case 1: handleOne(); break; case 2: handleTwo(); // NOTE: fallthrough *** case 3: handleTwoOrThree();
return statements Although it is allowed for a function to have multiple
returnstatements, in most situations it is preferable to funnel through a singlereturnstatement at the end of the function. An earlyreturncan be a clean option for a recursive base case or error handled at the beginning of a function.returncan also serve as a loop exit. However, scattering otherreturnthroughout the function is not a good idea —— experience shows they are responsible for a disproportionate number of bugs. It is easy to overlook the early-return case and mistakenly assume the function runs all the way to its end.Always include
{}on control statements: The body anif/else,for,while, etc., should always be wrapped in{}and have proper line breaks, even if the body is only a single line. Using braces prevents accidents like the one shown below on left.// ugh if (count == 0) error("not found"); for (int i = 0; i < n; i++) draw(i); if (condition) doFirst(); doSecond(); // inside? Indent looks so, but no braces!
// better if (count == 0) { error("not found"); } for (int i = 0; i < n; i++) { draw(i); } if (condition) { doFirst(); doSecond(); }
Booleans: Boolean expressions are prone to redundant/awkward constructions. Prefer concise and direct alternatives. A boolean value is true or false, you do not need to further compare to true/false or convert a boolean expression to true/false.
if (isWall == true) { ... } if (matches > 0) { return true; } else { return false; }
// better if (isWall) { ... } return (matches > 0);
Favor
&&, || , and!overand,or, andnot: For various reasons mostly related to international compatibility, C++ has two ways of representing the logical connectives AND, OR, and NOT. Traditionally, the operators&&,||, and!are used for AND, OR, and NOT, respectively, and the operators are the preferred ways of expressing compound booleans. The wordsand,or, andnotcan be used instead, but it would be highly unusual to do so and a bit jarring for C++ programmers used to the traditional operators.// non-standard if ((even and positive) or not zero) { ... }
// preferred if ((even && positive) || !zero) { ... }
Use error to report fatal conditions: The
errorfunction from the Stanford library can be used to report a fatal error with your custom message. The use oferroris preferred over throwing a raw C++ exception because it plays nicely with the debugger and our SimpleTest framework.// raw exception if (arg < 0) { throw arg; }
// preferred if (arg < 0) { error("arg must be positive!"); }