c++ code design rules and guidelines

UNDER CONSTRUCTION

Alexandre R.J. François
Adapted from: Lakos, 1996, ch. 2, Meyers, 1997, and other sources
Last modified: 04 October 2010

The beauty of any fine art comes not only from creativity but also from discipline. (Lakos, 1996, p. 63)

C++ allows for much creativity, which can result in poor quality performance in terms of usability, maintainability and performance. This document lists some rules and guidelines that, if followed, help improve code quality.

The most important rule: keep things as clear and simple as possible!

definitions

declaration vs. definition

A declaration introduces a name into a program

A definition provides a unique description of an entity (e.g. type, instance, function) within a program

internal vs. external linkage

A name has internal linkage if it is local to its translation unit and cannot collide with an identical name defined in another translation unit at link time

A name has external linkage if, in a multi-file program, that name can interact with other translation units at link time

logical vs. physical design

Logical design addresses only architectural issues

Physical design addresses organizational issues

component

A component is the smallest unit of physical design
In C++, one header file and one implementation file

A component is the appropriate fundamental unit of design

The logical interface of a component is that which is programmatically accessible or detectable by a client

The physical interface of a component is everything in its header file

ground rules

naming conventions

Be consistent about identifier names
Use uppercase letters to delimit words in multi-word names
(alternative is underscores)
Use a consistent method to distinguish type names from non-type names
Non-type (data and function) names begin with lowercase letter; type (entities that are neither data nor function) names start with an uppercase letter
Use a consistent method to distinguish class names
First letter capitalized, prefixed with capital C
Use a consistent method to highlight class data members
prefix with m_
Use a consistent method to highlight data types in variable names
n: integer, f or d: floating point, b: boolean, str: string, p for pointers, etc.
Use a consistent method to identify immutable values such as enumerators, constant data, and preprocessor constants
All uppercase with underscores
Be consistent about names used in the same way
Adopt consistent method names and operators for recurring design patterns such as iteration.
In C++, often i and j are integer indexes, p is a pointer; etc.
Use a consistent method to highlight global variables
prefix with g_

the global name space

Partition the global namespace
Use namespace

Global data

Avoid data with external linkage at file scope
No global variables

Free functions

Avoid free functions (except operator functions) at file scope in header files
Avoid free functions with external linkage (including operator functions) in implementation files

Enumerations, typedefs, and constant data

Avoid enumerations, typedefs, and constants at file scope in header files

Prepreocessor macros

Avoid using preprocessor macros in header files except as include guards
Prefer const and inline to #define

Include guards

Place a unique and predictable (internal) include guard around the contents of each header file
Place a redundant (external) include guard around each preprocessor include directive in every header file
Optional: important for very large projects

Names in header files

Only classes, structures, unions, and free operator functions should be declared at file scope in header files
Only classes, structures, unions, and inline (member or free operator) functions should be defined at file scope in header files

documentation

layout

Prefer space saving layouts
e.g. place opening bracket on same line as statement
Prefer C++ style comments
Organize class members by categories of funcionality
e.g. creators (bring objects into and out of existence), manipulators (non-const member functions), accessors (const member functions)
Sometimes grouping member functions into other categories can facilitate understanding of the basic functionality of an unfamiliar class.
Place implementation details (private and protected) ahead of public interface

documentation guidelines

Document the interfaces so they are usable by others
Have at least one other developer review each interface
Explicitly state conditions under which behavior is undefined
Document assumptions
Use of assert statements can help

documentation style guide

adapted from A Style Guide section in Sun's How to Write Doc Comments for the Javadoc Tool

When mentioned in descriptions, keywords and names should appear with the <code> style
Keywords, namespaces, class names, method names, field names, argument names, code examples, etc. should be placed between <code>-</code> tags.
Do not over-use in-line links.
Do not use parens in the general form of methods and constructors
The getVertices method ensures that the object returned is allocated rather than The getVertices() method ensures...
Use 3rd person (descriptive) rather than 2nd person (prescriptive).
Gets this fractal flake's subdivision level rather than Get this fractal flake's subdivision level
Method descriptions begin with a verb phrase:
Gets this fractal flake's subdivision level rather than This method gets this fractal flake's subdivision level
Class/interface/field descriptions can omit the subject and simply state the object:
A vector of vertices rather than This field is a vector of vertices
Use "this" instead of "the" when referring to an object created from the current class:
Gets this fractal flake's subdivision level rather than Gets the fractal flake's subdivision level
Write descriptions that add information to the entity names.

doxygen

Doxygen is a free, open source documentation system that supports the main documentation syntaxes in a variety of programming languages.

Doxygen can automatically generate reference manuals in several formats (including HTML and LaTeX) from a set of documented source files.

Doxygen parses the code to extract specially formatted comments that document code entities. In the process, Doxygen also extracts the structure of the code and the relations between the various code elements. These relations can be visualized through automatically generated dependency graphs, inheritance diagrams and collaboration diagrams.

Doxygen can also be used to generate human-written documentation (beyond the automatically generated reference guide).

The Doxygen website has a list of projects that use Doxygen for their documentation, and a short list of particularly nice examples.

classes: logical design rules

inheritance (is-a)

Implement is-a relationships through class inheritance
Liskov's substitution principle
An instance of a subclass can be substituted for an instance of the superclass without causing any problem
Move common interface, data, behavior as high as possible in the inheritance tree
Make all data private, not protected
Prefer polymorphism to extensive type checking
Overridden virtual functions
Never redefine an inherited non virtual function
Don't re-use names of non-overridable base class routines in derived classes
Never redefine an inherited default parameter value
Be suspicious of classes that override a routine and do nothing inside the derived routine
Avoid deep inheritance trees
Avoid private inheritance
Unless you know what you are doing
Avoid multiple inheritance
Unless you know what you are doing

member functions and data

Implement has-a relationships through containment
Keep class data members private
Principle of encapsulation, aka information hiding
No data members in the public interface
Avoid returning "handles" to internal data
minimize indirect routine calls to other classes
law of demeter: an object can call its own routines and the routines of objects it creates, but should avoid calling routines of objects provided by the objects instantiated.
Know what functions C++ silently writes and calls
Copy constructor, assignment operator, destructor, address-of operators, default constructor (if none provided) – all public!
Disallow implicitly generated member functions and operators you don't want
Make private

constructors and destructors

Declare a copy constructor and an assignment operator for classes with dynamically allocated memory
Prefer initialization to assignment in constructors
Initialize all member data in all constructors, if possible
Prefer deep copies to shallow copies until proven otherwise

assignment operators

Have operator= return a reference to *this
Assign to all data members in operator=
Check for assignment to self in operator=

components: physical design rules

Logical entities declared within a component should not be defined outside that component.
Avoid definitions with external linkage in the implementation file of a component that are not declared explicitly in the corresponding header file

headers

The implementation file of every component should include its own header file as the first substantive line of code
Clients should include header files providing required type definitions directly
Except for non-private inheritance, avoid relying in one header file to include another
Avoid accessing a definition with external linkage in another component via a local declaration
Instead, include the header file for that component

file names

The root names of the implementation and header files that comprise a component should match exactly

dependencies

Avoid cyclic physical dependencies among components
Acyclic physical dependencies can dramatically reduce link-time costs associated with developing, maintaining and testing large systems

references

(Lakos, 1996) John Lakos, Large Scale C++ Software Design, Addison-Wesley Professional, 1996, ISBN-13: 978-0201633627.

(Meyers, 1997) Scott Meyers, Effective C++, Second Edition, Addison-Wesley Professional, 1997, ISBN-13: 978-0201924886

Sun, Java documentation: How to write comments for the JavaDoc tool

Doxygen website: www.Doxygen.org

arjf © 2010