Identifying all the components contributing to a well-designed program can be challenging. The following list serves as a starting point and is used to evaluate class projects. While there may be some exceptions, they are not typical and should not overshadow the general guidelines. Consider how these guidelines apply when reviewing a program rather than focusing on potential exceptions. The checklist will be extended as the semester progresses.
Only source files belong in the repository:
Generated files do not belong in the repository:
Generated files take up a lot of file space and prevent others from easily cloning and building the repository.
Do not use using namespace std;
.
using namespace std; cin >> n;
Instead, use the prefix for everything in a namespace:
Code | Explanation |
---|---|
Single space after start of comment | |
Single space after keywords of statements | |
Single space before operators | See above |
Single space after operators | See above |
No space between call name and argument list | |
No space before statement-end semi-colon |
Use the following naming convention:
Category | Semantics | Convention | Example |
---|---|---|---|
Classes | things not actions | PascalCase | class Token |
Fields/Variables/Parameters | things | camelCase | int totalSize; |
C++ Methods/Functions | actions | camelCase | void sortNames(); |
C Functions | actions | under_score/camelCase | srcml_archive_get_unit() |
Constant scalar values | things | UPPERCASE | const int MAX_SIZE = 100; |
C-Preprocessor/cpp variables | things | UPPERCASE | #ifndef INCLUDE_XML_HPP |
std::vector
, follow that naming convention.XMLBuffer
Single-letter variable names are not to be used, except in certain, well-understood situations:
Name | Purpose |
---|---|
i , j , k |
loop-control variables |
c |
general character |
s |
general string |
n |
general int |
x |
general double |
Initialize temporary variables (those with local scope) when declared whenever possible.
Initialization in Declaration (preferred) | Separate declaration |
---|---|
Exception: Temporary variables initialized as an argument in a call:
int
as the standard C++ integer type. Possible exceptions: size_t
, std::size_type
double
as the standard C++ floating-point number type. There is hardly any reason to use float
.Comment each section of code on the line before
const
whenever possibleauto
when initializing a declaration that includes a call, e.g., auto pc = buffer.begin()
Use an explicit type instead of auto
when initializing with a literal value to make certain you get the intended type
Prefer pre-increment over post-increment, i.e., prefer ++i;
over i++;
. Pre-increment is easier to understand, explain, and implement, e.g., post-increment is often implemented using pre-increment, especially when these operators are overloaded:
Pre-increment Implementation | Post-increment Implementation |
---|---|
This also applies to pre-decrement and post-decrement
Prefer pre-increment over post-increment in for loops. Using pre-increment in a for loop does not mean that the increment occurs before the loop.
for-statement | Equivalent while-statement |
---|---|
Favor the range-based for-statement instead of direct indexing (or iteration).
Range-based for (preferred) | Direct Indexing |
---|---|
@param
For each parameter@return
For the general return purpose@retval
Specific return values, e.g., error or status codesThere is much more, so feel free to experiment. However, be consistent.
inline
specifier. First, the compiler can ignore it. Second, the compiler automatically inlines small functions. Third, it is overused. The inline
specifier has good purposes, but only in specific cases and not in this course. Note: Do not confuse the general programming term "inline a function" or "inline a method" with the inline
specifier.Use the following tables for type T
:
Direction | Primitive | Object |
---|---|---|
IN | T | const T& |
OUT, IN/OUT | T& | T& |
Examples:
std::string
vs. const std::string&
IN parameter, especially when passed repeatedly.const int,
is not in the list. The reason is that the const
is an unnecessary restriction for the function/method definition (implementation) and does not change the arguments allowed (caller).T
)std::string&&
, separatelyFor free functions:
Type | Application | Example |
---|---|---|
T | Return copy of a local variable | |
T | Return copy of a parameter | |
const T& | Return a read-only reference to a parameter |
T&
. Be very careful about this. It's safer to send a copy and may be just as efficient.std::string&&
, separatelyThe following applies to all comments except file header comments.
Prefer the default and methods common to a container:
Prefer | Explanation | |
---|---|---|
By default a std::string is an empty string | ||
A std::string is a (specialized) container. Prefer container methods over comparison. One exception might be in a set of if statements (such as a nested if) that compares the string to other literal strings. | ||
Prefer container method to assignment |
Prefer std::string_view
if referring to an existing string or part of a string.
One of the problems with const std::string&
parameter is that it makes a copy of a literal c-string.
Declaration | Call | Memory |
---|---|---|
void f(const std::string& s); |
std::string s; f(s); |
Refers to the existing string |
void f(const std::string& s); |
f("abc"); |
Creates a std::string copy |
The string type std::string_view
was an addition to C++ in C++17. It allows you to efficiently refer to parts of a pre-existing std::string
or a c-string char*
without making a copy. It has many of the same methods as the std::string
, and when the memory for the string already exists, it just refers to it, and no copies are made:
Declaration | Call | Memory |
---|---|---|
void f(std::string_view s); |
std::string s; f(s); |
Refers to the existing string |
void f(std::string_view s); |
f("abc"); |
Refers to the existing string |
The IN parameter of a std::string_view
is passed by value instead of const reference as it consumes only a small amount of memory.
Write debugging/tracing messages to std::cerr
, not std::cout
:
std::cerr for error output |
---|
std::cout
for output, so this gives a way for the user to separate the intended output from error messagesstd::cerr
is not buffered, while std::cout
is. If your program crashes, any unbuffered output may be lost. So, you may not see your debugging messages.