C++ Style Guide
Each coding assignment must follow this standard. Using one style consistently throughout the source code lets us focus on other (more important) issues.
There’s no one, right way to write code. But there are a lot of wrong (or, at least, bad) ways. Certainly, it is possible to write “correct” code that violates the guidelines below. Even so, we ask that you adhere to these conventions for CSCI 235 to motivate consistency and readability in your personal coding style.
Companies typically adopt their own, company-wide conventions for style. 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.
General Guidelines
- All code should compile without warnings using the
-Wall
and-Wextra
parameters. - No non-constant global variables.
File Names
All C++ source code files need to end in .cpp. Additionally, they should only contain letters, numbers, and underscores (no spaces) and they should NOT start with a number (start filenames with a letter).
The following are good filenames:
launchSequence.cpp
self_destuct.cpp
Assignment02_P1.cpp
The following are BAD filenames:
launch sequence.cpp
(contains a space)self_destuct
(does not have the .cpp extension)02Problem1.cpp
(starts with a number)Assignment02.1.cpp
(contains a period before.cpp
)AutoTuner.cpp.cpp
(contains a period before the extension)
Comment
Use the /* */
syntax for multi-line comments and //
for single-line comments.
Program Description
Atop each file, include a commented header with a brief description of the program or file and the programmer's name. Write the description in your own words.
For example:
/*
* Simulates the count to launch an Apollo rocket. The countdown starts
* at 10 and continues down to 0 (takeoff). This includes the main-
* engine start at T-6.6 seconds.
*
* by Dr. Hayes
*/
Commenting Blocks of Code
Comments should be present throughout your code. They make code more readable, not only for others like your instructor but also for you.
Good example:
const double TEMP_RATIO = 5.0 / 9.0; // ratio of Celsius to Fahrenheit
// convert Fahrenheit to Celsius
double celsius = TEMP_RATIO * (fahr - 32.0);
Place comments on their own line(s), preceding whatever you are explaining, or on the same line (if it is very brief), but not on the next line. In other words, don't do this:
Bad example:
double celsius = TEMP_RATIO * (fahr - 32.0);
// convert Fahrenheit to Celsius
How much and how often to comment? Commenting too little is bad. Commenting too much is bad. Commenting every few lines of code (i.e., interesting blocks) that actually do something is a decent rule of thumb. Put a comment above every code block (e.g., above if statements, loops, etc.). Also, try to write comments that explain the meaning of non-obvious code and describe why you implemented a block in a certain way. Keep comments short (try to keep them to 1 line each, unless you are explaining something complex).
Control-Structure Comments
Each control structure (if...else, switch statement, loop, etc.) should be directly preceded by a comment.
Good example:
// If we find a lowercase letter, capitalize it.
if (letter >= 'a' && letter <= 'z')
{
letter += 'A' - 'a';
}
Function Prototype Comments
Each function created by you (except main()
) should have a comment above its declaration (prototype). More on this in the Functions section.
Comments need to be in the bodies of every multi-line function, not just main()
.
Commenting Out Code
When working on code, programmers tend to "comment out" lines of code to exclude them from the program without deleting them. It's fine to do this while you work on your program, but it is considered bad style to turn in a file with blocks of code commented out. If the program is done and such code is not needed, just remove it.
Whitespace and Indentation
Use a single space between operators and their operands:
count += (point1 - point2) * unit / time + random();
count+=(point1-point2)*unit/time+random();
Indentation
Indent your code with one hard tab (or four spaces) at a time to make clear which blocks of code are inside of others.
WARNING
If you use your keyboard's Tab key to do so, be sure that your text editor is configured to convert tabs ('\t'
) to an actual tab (i.e. a hard tab) or four spaces; otherwise, your code may not print or display properly on someone else's computer. The tab key renders differently in different editors.
TIP
In Visual Studio Code, take a look at the blue status bar at the bottom. To the right, you will see "Spaces: " or "Tab-Size", which indicates if you are indenting with & number of spaces or with tabs receptively.
Increase your indentation by one increment on each curly brace
{
, and decrease it once on each closing brace}
.Place a line break after every
{
.
Here are examples of well-indented code and bad indentation:
int main()
{
int val = 0;
if (val > 0)
{
cout << "The value is positive." << endl;
}
else
{
// Display 3 stars
for (int iter = 0; iter < 3; iter++)
{
cout << "*";
}
}
return 0;
}
int main()
{
int val = 0;
if (xval > 0)
{
cout << "The value is positive." << endl;
}
else
{
// Display 3 stars
for (int iter = 0; iter < 3; iter++)
{
cout << "*";
}
}
return 0;
}
TIP
Visual Studio Code will automatically re-indent select code if you press Shift + Alt + F
on Windows or Shift + Option + F
on macOS.
Xcode will automatically re-indent the selected code if you press Control + I
(or choose Editor > Structure > Re-Indent from the file menu).
New Lines
Always put a newline after a semicolon (we'll talk about one exception to the rule when we get to for loops).
Bad:
int age; double weight, height; string name;
cout << "Enter your age: "; cin >> age; // Get the user's age
Good:
int age;
// Get the user's age
cout << "Enter your age: ";
cin >> age;
Do not place more than one statement on the same line.
Nothing goes on the same line after an opening curly brace ({
).
Noting goes on the same line before a closing curly brace (}
).
Bad:
int main()
{ cout << "Hi" << endl;
return 0; }
Good:
int main()
{
cout << "Hi" << endl;
return 0;
}
Long Lines
If a statement is really long (more than 80 characters), split the statement into multiple lines, with each subsequent line indented.
This is bad:
cout << "This is a really long line " << 5 * 10 / denom << " and more text." << endl;
This is how the previous line should look:
cout << "This is a really long line "
<< 5 * 10 / denom
<< " and more text." << endl;
Good example of a function prototype:
void reallyLongFunctionName(int with, int lots,
int of, int parameters);
TIP
To add a line to Visual Studio Code indicating the 80-character mark, go to File > Preferences > User/Workspace settings Add this line to your custom settings on the right: "editor.rulers": [80],
Variables and Constants
Identifiers
In programming languages, names you give to things are called identifiers.
Identifiers should be meaningful, such as
numberOfGames
. Identifiers likeherp
andderp
are funny, but not helpful.A single letter cannot be a descriptive identifier. Avoid even the commonly used loop variables like
i
andj
. Good alternatives areindex
oriter
.Bad example names:
cppint x, y, i, clr lineW;
Good example names:
cppint xCoord, yCoord, index, color, lineWidth;
Unless constant or a new data type (see below), all identifiers should start with a lowercase letter and each subsequent word should start with a capital letter (i,e., camel case).
Bad examples:
cppint Time; // Should start with a lowercase letter. double percent_increase; // Should be camel case. string FirstName; // should start with a lowercase latter.
Good examples:
cppint time; double percentIncrease; string firstName;
Naming Constants
Constants should be named in all caps with each word separated by an underscore (_
).
Good examples:
const double PI = 3.14159;
const double INCH_TO_CM = 2.54;
const double CM_TO_INCH = 1.0 / INCH_TO_CM;
const int MAX_SCORE = 1000;
Variable Declaration and Assignment
Declare variables in the smallest scope possible.
Choose appropriate data types for your variables. If a given variable can store only integers, give it type
int
rather thandouble
. If a variable stores characters, make it achar
instead of anint
.Never use
0
and1
withbool
variables.true
andfalse
exist for a reason.Initialize variables at declaration when appropriate:
cppint count = 0;
In other words, don't do this:
cppint count; ... count = 0;
If a variable's value doesn't change throughout the code, it should be declared as a constant and be named in all caps
LIKE_THIS
:cppconst int NUMBER_OF_PROJECTS = 5;
Global Variables and "Magic" Numbers
Non-constant global variables (those declared outside of functions, including
main()
) must never be used. They lead to unintentional bugs and are often caused by poor design. Instead, you should be passing variables via functions.Global constants are acceptable when needed.
Magic numbers are seemingly random constants within your program, numbers whose appearance in code isn't clear from the outset. You should avoid magic numbers at all costs. Fortunately, C++ provides us with an easy way to do so by storing such numbers in constants. So, don't write:
cppdouble area = 3.141592 * radius * radius; // Magic number!
Instead, store the number in a constant:
cppconst double PI = 3.141592; double area = PI * radius * radius;
However, don't just restate the value with your identifiers:
cppconst int TWO = 2; ... if (n % TWO) ...
This adds no meaning to your code.
The reason is that we want to make it clear both to the compiler and to readers of the program that this quantity can't be changed. The compiler can often produce better machine code with that knowledge. And the reader gets a more precise understanding of the program's behavior. Moreover, if the programmer (you!) decides to ever change that value, he or she will have to do that just in one place!
Conditionals
Always use curly braces (even when not necessary) and use a consistent brace style. In other words, do this:
cppif (val > 0) { cout << val << " is positive." << endl; }
But avoid doing this:
cppif (val > 0) cout << val << " is positive." << endl;
and
cppif (val > 0) { cout << val << " is positive." << endl; }
Don't ever test whether a
bool
value is==
or!=
totrue
orfalse
. Don't docppif (isValid == true)
or
cppif (isValid == false)
Do check if a variable is
true
orfalse
as in:cppif (isValid)
or
cppif (!isValid)
If you have an if/else statement that returns a
bool
value based on a test, such as this one:cppif (score1 == score2) { return true; } else { return false; }
simplify it with a single statement that just directly returns the test's result:
cppreturn score1 == score2;
Variable Assignments Within Conditionals
Having assignments within conditional expressions for loops or if statements can lead to code that is difficult to read, understand, and maintain.
This is bad:
int num;
if (cin >> num)
{
//...
}
Good example:
char num;
cin >> num;
if (num)
{
//...
}
There are several reasons why it's considered bad practice:
Readability: Code should be written in a way that is easy to understand for other developers (including your future self). Placing assignments within conditional expressions can make the code harder to read because it adds complexity to the logic flow.
Potential Bugs: Complex conditional expressions increase the likelihood of introducing bugs into the code. It's easy to make mistakes when mixing assignment and condition evaluation, leading to unexpected behavior.
Debugging Difficulty: When an assignment and a condition are mixed, it can be challenging to debug the code because you're not sure whether the assignment is intended to be part of the condition or not.
Violation of Single Responsibility Principle: Assignments inside conditionals often indicate that a single line of code is trying to do too much. It's generally better to separate concerns and keep each line of code focused on a single task.
Maintainability: Code that is difficult to read and understand is also difficult to maintain. Other developers (or even yourself in the future) may have a hard time modifying or extending the code if assignments and conditions are intertwined.
Potential Side Effects: Assignments within conditional expressions can lead to unintentional side effects. For example, if the assignment is part of a loop condition, it may modify loop variables unexpectedly, leading to unexpected behavior.
Instead of mixing assignments and conditions, it's better to separate them to improve readability, maintainability, and reduce the likelihood of introducing bugs. Write clear and concise code that clearly expresses the intent of each line, making it easier for others to understand and maintain.
Switches
Indent your switch statements like this:
switch (val)
{
case -1:
cout << "value is -1" << endl;
break;
case 1:
cout << "value is 1" << endl;
break;
default:
cout << "value is neither -1 nor 1" << endl;
break;
}
The case statements are each on their own lines, with a newline after the colon. Everything statement within a case statement is indented one more.
Loops
Whenever you a counting variable, use more specific names than
i
,j
, etc.cppfor (int rowIter = 0; rowIter < rowCount; rowIter++) { for (int colIter = 0; colIter < colCount; colIter++) { // do something } }
If you find yourself in need of more than three levels of nested loops, you should reconsider your design!
A
for
loop should look like the one above, while awhile
loop should resemble the one below:cppwhile (num < 0) { cin >> num; }
Avoid
break
statements and never usecontinue
statements in loops. Loops should be controlled by a condition and should terminate when that condition is met. Generally,break
should only be used inside of aswitch
.Never write endless loops, such as
while (true)
.Use a
for
loop when the number of repetitions is known (definite); use awhile
loop when the number of repetitions is unknown (indefinite).cpp// repeat exactly size times for (int iter = 0; iter < size; iter++) { // do something } double value; // repeat until a valid number is entered. cin >> value; while (!cin) { // Handle input error and get next input here. }
Functions
Like non-constant variables, function names should be camel case (i.e., start with a lowercase letter and each subsequent word should start with a capital letter).
Value-returning functions should be named after what they return and void functions after what they do.
Good examples:
bool isPrime(int num);
void displayStats (double average, int median, int min, int max);
void spinSpinner (int numberOfTurns);
Always include function prototypes before the main()
function and function definitions below main()
.
Every function except main()
should have a function prototype that is declared before main()
. Function definitions should be defined below main()
.
Function Prototype Comments
Each function prototype should be directly preceded by a comment describing what the function does and how to use (call) it. The main()
function does not need a comment directly above it because it is not a prototype and the comment at the top of the file already explains what the program does. Of course, you should always include comments inside of the function body.
Good example:
/**
* A function that draws a spinning animation
* @param numberOfTurns the number of times the spinner spins.
*/
void spinSpinner(int numberOfTurns);
Function Decomposition
Often you might find yourself writing similar blocks of code. Utilize functions to decompose your program and minimize redundancy in code.
cppcout << "What is your first name?" << endl; cin >> firstName; ... cout << "What is your last name?" << endl; cin >> lastName; ... cout << "What is your email?" << endl; cin >> email;
Instead, create and use helper functions that incorporate the common functionality:
cppfirstName = getInfo("What is your first name?"); ... lastName = getInfo("What is your last name?"); ... email = getInfo("What is your email?"); ... string getInfo(const string& PROMPT) { cout << PROMPT << endl; string input; cin >> input; return input; }
If you have a single function that is very long, break it apart into smaller sub-functions. The definition of "very long" is vague, but let's say a function longer than 40-50 lines is pushing it. If you try to describe the function's purpose and find yourself using the word "and" a lot, that probably means the function does too many things and should be split into sub-functions.
Enums and Structs
- Use
PascalCase
for enum, struct, and class names. (Notice how the first letter is capitalized.) - A comment should directly precede each enum or struct declaration describing the purpose of the data type.
- The values of an enum should named in all caps with each word separated by an underscore (
_
). See the standard for naming constants.
Platform Specific Code
All programs should compile and run on any system. We want our code to be "portable", meaning able to be compiled and run on multiple systems.
Avoid System Calls
A system call is when your program calls another program on the system. In C++ this is commonly done through a function called system()
. These calls are tempting because they provide additional features to your program without having to learn the programming to do them.
Bad:
system("PAUSE"); // Windows only way to pause execution.
system("CLS"); // Windows only way to clear the screen.
Good example to pause execution using C++
cout << "Press enter to continue . . .";
cin.ignore(INT_MAX, '\n'); // ignore everything typed until user hits enter.
Non-standard libraries / includes
Avoid the use of operating system-specific libraries. We want code that compiles on all operating systems.
Example of Bad Code:
#include <windows.h>
Use of the following standard libraries are encourage:
- cassert
- cctype
- cfloat
- chrono
- climits
- cmath
- cstddef
- cstdlib
- cstring
- ctime
- fstream
- string
- iostream
- iomanip
- ostream
- random
- span
- thread
Other
- C++ contains an
exit()
function that immediately exits your entire program. You should never call this function in this course. Your program should always exit naturally by reaching the end of yourmain
function and returning. - Never use
goto
to go to another line of code. Instead, make use of functions and loops! - Never use
stringstream
for this course.