With an algorithms and data structure course in C++ imminent, it seemed like the right time to develop a gut instinct for clean, maintainable C++ code. To that end, I've gathered some resources on C++ programming:
- (PDF) UofM EECS 381 Coding Standards
- (PDF) Tips for Optimizing C/C++ Code
- (Github) C++ Best Practices
- (PDF) List of STL algorithms
As I complete each project in EECS 281 (Data Structures and Algorithms), I'll work through this checklist:
-
Initialized Vectors When size is known, allocate vector size with
resize(n)
orreserve(n)
. -
No Synchronized IO Since we know we won't be using C-style IO, disable syncing with
std::ios_base::sync_with_stdio(false);
. -
Buffered Output If outputting a lot of data, end with
"\n"
instead ofstd::endl
to avoid flushing the buffer at the end of every line. -
Single Point of Maintainance Unique code only appears for unique features, program constants are names as
const variables
, functions are used instead of duplicated code -
No Unsigned Integers only
size_t
is used, and often cast to anint
. -
Double v. Float double used instead of float; equality is NEVER used.
-
Clear Class and Var names Upper case name for my own types; no
victims
.using
used when appropriate._c
for constants,_t
for typedefs. Clear variable names, without implementation details -
Class Structure Not mixing plain old data and modifying classes (eg. Circle is POD, Geometry class manipulates it).
-
String Literal Constants string constants are defined as
const char * const
-
Using nullptr Use
nullptr
, andif (!ptr)
. -
std::string Use
+=
whenever possible. Use standard overloaded operators, notstrcmp
. -
pre-increments Use pre-increments in for loops with iterators:
for(list<Thing>::iterator it = things.begin(); it != things.end(); ++it)
-
Catching exceptions Avoid catching
std::exception
as a blanket error. Always catch errors by reference:catch (Error& x)
. -
Using functions Freely use functions, with clear names. Inline small functions for performance. Avoid swiss-army functions or flags.
-
Passing parameters If you don't want modification, pass built-in-types by copy, and other types by reference to const, or const pointers.
-
Scopes of variables Declare variables in the narrowest scope where it is used. Declare simple types inside loops, and complex types outside those loops.
-
Loops, If/Else and Switch Use flat if/elses, or switches when possible. Minimize function calls in loops to allow compiler optimizations:
for(size_t i=0, len=list.size(); i < len; i++)
-
File IO Format should be
while(infile >> x) { /* do stuff */ }; if(!infile.eof()) { /* error handling */ }
-
Error Handling Explicitly design for errors. Elegantly handle user error, and use
assert
for programmer errors. No Exceptions for normal control flow. -
Class Design Base class functionality should be used by all derived classes. Do not share data in static member variables. Declare public, protected, then private. All member vars should be private, and const functions should be used.
-
Constructors Single argument constructs should use a default parameter for the default constructor, and constructors should be marked as explicit unless class is POD:
explicit Thing(int i_= 0) : i(i_) {}
. -
Ahmadal's Law & Premature optimizations make the common case fast, and the rare case correct. Code for correctness before optimizing.
-
Jumps and Branches Use function calls sparingly, prefer iteration over recursion, move loops inside function calls.
-
Arrays, local vars and function parameters Order array indices in order whenever possible to aid the CPU. Avoid local variables and function parameters so that they can be stored in registers.
-
Default constructors and class operations Make default constructors cheap when possible, especially for classes manipulated frequently. Use
+=
instead of+
for classes, and the opposite for primitive data types. -
Equations and early terminations Simplify all equations on paper beore implementing them in code. Keep the most common cases in the first if statement, or make loops terminate faster in those cases.
-
Extra GCC flags Consider using
-Wall -Wextra -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wunused -Woverloaded-virtual -Wpedantic -Wmisleading-indentation -Wduplicated-cond -Wduplicated-branches -Wnull-dereference -Werror
-
Default initializing Default initialize variables with braces:
int m_value{ 0 };
-
Avoiding [] Consider [] as an indication that an algorithm was not used where it could have been
I hope to slowly check items off this list as they are internalized- as a personal goal by the end of the course I would like to do all of these things before I even go through the list. At that point, I will have internalized best practices!