C++ Style Recommendations

In writing code to implement a design, it is as important to write
it to be read as it is to write it to be executed.
In many software projects, maintainance is perhaps 50% of the cost
of the total project, so the engineers altering the code have to
be assisted in understanding the design and implementation.
C++ is a bulky language, having inherited (!) all the baggage of
C with the addition of a complex class structure. As with most
languages, your designs will be better realized if you do not
use all of C++ capabilities... use of many language features will
result in code that difficult to fathom.
This list contains recommended C++ techniques that will make your code
more uniformly readable, understandable, and maintainable.
These guidelines are not inviolable rules, but if followed
as closely as possible will result in a better realization of your designs.
-
Use pointers to represent classes... avoid using statically
allocated objects.
Languages like Smalltalk and Java do this implicitly.
Some go so far as to do this:
typedef class FooBarInfo * FooBar ;
class FooBarInfo {
... guts of "class" Foobar ...
}
-
Avoid use of friend declarations
Violates encapsulation, though it is sometimes necessary/very helpful;
adds additional complexity, so make sure its use is necesasary before using
-
All data members should be private/protected
For effective encapsulation
-
Use const where possible
Const methods indicate no changes will take place to the object...
compiler will enforce this "declaration" as a check on your design
-
Avoid non-class storage
If you need global vars, etc., use the singleton pattern to create
that storage in a class.
-
Explicitly define a constructor for each class
Do not rely on the hidden, default creations of the compiler...
make the code explicitly reflect the run-time reality.
-
Explicitly define a destructor for each class with a constructor
provides hooks for future changes, as well as breakpoints for debugging.
-
Use inheritance to express natural hierarchies
They are usually shallow.
-
Intermediate classes in a hierarchy should be abstract
Makes evolving the hierarchy easier.
-
If the hierarchy is not natural, use delegation to obtain "inherited"
behaviors.
Also gain the dynamic advantage of being able to replace behaviors at run-time.
-
Do not use multiple inheritance
truly useful in few situations. Complex semantics, takes deep compiler
understanding to know fully what methods, constructors, etc. get called
when.
-
Use overloaded methods and default parameters sparingly.
Make sure the overloading makes good sense, really reduces the comnplexity
of the code:
void setColorByname(const char* color);
void setColorByIndex (int pixel);
void setColorByRGB(float red, float green, float blue);
void setColorByRGBValue(unsigned r, unsigned g, unsigned b);
This is a good candidate to overload...
-
Make a class responsible for its own data
Clean up allocated storage on object destruction.
Check array indices against bounds as much as possible
to avoid run-time errors.
-
Always use public inheritance.
Makes the hierarchy much simpler to understand.
Note C++ assumes private inheritance... you have to remember
to add "public" keyword to subclass declarations:
class SubC : public Super { ... }
Declaring a subclass to have it's parent class private means the
public parts of the parent are not public in the subclass.
-
Use virtual methods almost exclusively
These can be overridden in subclasses. Makes dynamic semantics
more likely to match your intentions. If not virtual, compiler
must decide statically which method to call if SuperC, SubC have
mathod by same name... often means parent method and that is not
what you usually want. C++ default is non-virtual.
-
Make destructors virtual throughout a class hierarchy