User Tools

Site Tools


cppbp

Most of this is taken from the books

  • C++ Coding Standards - 101 Rules, Guidelines and Best Practices
  • Effective C++
  • Thinking in C++
  • C++ Cookbook

General

Conventions

  • Never use “underhanded names” (ones that begin with an underscore or that contain a double underscore)
  • Always use ONLY_UPPERCASE_NAMES for macros and never think about writing a macro that is a common word or abbreviation (including common template parameters, such as T and U; writing #define T anything is extremely disruptive
  • Prefer using named variables, not temporaries, as constructor parameters

Compile Without Warnings

Be sure your code compiles at the highest compiler warning level. eg.

.

-Wall -ansi -pedantic -Werror

To turn of warnings coming from headers you can not change use (Unfortunately this is for VC2005 and does not work for g++.) :

.

#pragma warning(push)     // disable for this header only
#pragma warning(pop)      // restore original warning level

.

#pragma warning(disable:4512) #pragma warning(disable:4180)
#include <boost/lambda/lambda.hpp>

Design Style

Clarity

  • Use the simplest technique that works.
  • Correct is better than fast.
  • Simple is better than complex.
  • Clear is better than cute.
  • Safe is better than insecure.

Algorithm Complexity

  • Prefer to use linear algorithms or faster wherever possible:
    • Constant-time complexity, such as push_back and hash table lookup, is perfect.
    • O(log N) logarithmic complexity, such as set/map operations and lower_bound and upper_bound with random-access iterators, is good
    • O(N) linear complexity, such as vector::insert and for_each, is acceptable.
  • Avoid Premature Optimization: It is much much easier to make a correct program fast than it is to make a fast program correct.

But Dont Write Inefficient Code!

  • Use pass-by-reference where appropriate
  • Use ++x instead of x++ where possible. (Especially with iterators!)
  • Use the initializer list instead of assignment in the constructor
  • It is not a premature optimization to reduce spurious temporary copies of objects, especially in inner loops!
  • It can sometimes be beneficial to hoist a variable out of a loop. Most of the time that won't obfuscate the code's intent at all, and it can actually help clarify what work is done inside the loop and what calculations are loop-invariant.
  • Prefer to use premade algorithms instead of explicit loops! (http://www.cplusplus.com/reference/algorithm/)
    • for_each (or BOOST_FOREACH )
    • find / find_if
    • count_if
    • copy
    • replace_copy_if / remove_copy_if (to filter a collection)

Avoid Global and Shared Data

  • Avoid data with external linkage at namespace scope or as static class members. These complicate program logic and cause tighter coupling between different (and, worse, distant) parts of the program.
  • Strive for “shared-nothing;” prefer communication (e.g., message queues) over data sharing.
  • Prefer low coupling and minimized interactions between classes.
  • A common example is to never expose data members of class types by making them public

Ensure Resources are owned by objects

* C++ : “resource acquisition is initialization” (RAII) * When allocating a raw resource, immediately pass it to an owning object: Prefer to hold dynamically allocated resources via smart pointers instead of raw pointers. Perform every explicit resource allocation (e.g., new) in its own statement that immediately gives the allocated resource to a manager object (e.g., shared_ptr). If you use a Smart Pointer you are TAKING OWNERSHIP of the object! ' (→ dont use smart pointers for resources YOU did not allocate. The Resource WILL be deleted when your Smart Pointer disappears). * Never allocate more than one resource in a single statement. (Exceptions!) * To avoid unwanted copies make all objects extend boost::noncopyable by default * Be conscious of copy construction and assignment the compiler-generated versions probably won't be correct. * When using shared_ptr be wary of 'circular references (use weak_ptr to avoid). ====== Coding Style ====== ===== Avoid Macros ===== * Except for Include Guards and Conditional Compilation! ===== Static Type Checking ===== * Prefer to write code that uses the compiler to check for invariants during compilation use const avoid void* avoid unsafe casts

Const

* Use const whereever possible * When a const member function of a class legitimately needs to modify a member variable declare that member variable mutable * Never cast away const except to call a const-incorrect function * Avoid const pass-by-value function parameters in function declarations (they are confusing and useless to the reader of the header file). Const pass-by-value function parameters can (and should) be used in the functions definition however!

Variables

* Avoid Magic Numbers, use constants

.

class Muh {
    static const int DEFAULT_COW_SIZE = 400;
};
  • Declare Variables as locally as possible (close to where it is used) * Always Initialize Variables!

Functions

* Replace complicated computational flows with functions easier to Read easier to Test * Avoid long functions and deep nesting Every function should be a coherent unit of work bearing a suggestive name Give one function one responsibility Don't repeat yourself: Prefer a named function over repeated similar code snippets (Code Smells!) Prefer &&: Avoid nested consecutive ifs where an && condition will do. Prefer automatic cleanup via destructors over Try blocks Prefer algorithms: They're flatter than loops (http://www.cplusplus.com/reference/algorithm/) Don't switch on type tags. Prefer polymorphic functions ===== Avoid Cyclic Dependencies ===== * Good Design * Forward Declarations! (see the Chapter in “Effective C++” ) ===== Make Headers self-sufficient ===== * Ensure that each header you write is compilable standalone, by having it include any headers its contents depend upon. * But don't include headers that you don't need * Make sure each Header has an INCLUDE GUARD! Use a unique guard name

. #ifndef FOO_H_INCLUDED_ #define FOO_H_INCLUDED_ contents of the file #endif ====== Functions and Operators ====== ===== Don't write code that depends on the order of evaluation of function arguments ===== The order in which arguments of a function are evaluated is unspecified, so don't rely on a specific ordering. Example: . void Transmogrify( int, int ); int count = 5; Transmogrify( ++count, ++count ); order of evaluation unknown!

The cure is simple: use named objects to enforce order of evaluation!

==Parameter Passing== * Don't use C-style varargs! Input Parameters' * Always const-qualify all pointers or references to input-only parameters. * Prefer taking inputs of primitive types (e.g., char, float) and value objects that are cheap to copy (e.g., Point, complex<float>) by val. * Prefer taking inputs of other user-defined types by reference to const. * Consider pass-by-value instead of reference if the function requires a copy of its argument 'In/Out Parameters * Prefer passing by (smart) pointer if the argument is optional (so callers can pass null as a “not available” or “don't care” value) or if the function stores a copy of the pointer or otherwise manipulates ownership of the argument. * Prefer passing by reference if the argument is required and the function won't store a pointer to it or otherwise affect its ownership. This states that the argument is required and makes the caller responsible for providing a valid object.

Operators

Overload operators only for good reason, and preserve natural semantics; if that's difficult, you might be misusing operator overloading.

  • Named functions are less likely to have assumed relationships, and therefore should be preferred for clearer code if there can be any doubt about semantics. * If you overload + ,also overload += , if you overload * also overload *= (etc.) * Avoid overloading &&, |, or , (comma). The built-in &&, | , and, (comma) enjoy special treatment from the compiler. If you overload them, they become ordinary functions with very different semantics. * Remember: You cant change operator precedence!

Class Design and Inheritance

“The most important single aspect of software development is to be clear about what you are trying to build.” –Bjarne Stroustrup

Be Clear what kind of class you are going to write

Value Class

* Has a public destructor, copy constructor, and assignment with value semantics. (* Has no virtual functions (including the destructor).) * Is intended to be used as a concrete class, not as a base class. * Is usually instantiated on the stack or as a directly held membr of another class.

Base Class

Base classes are the building blocks of class hierarchies. * Has a destructor that is public and virtual or else protected and nonvirtual * Has a nonpublic copy constructor and assignment operator (use boost::noncopyable!) * Establishes interfaces through virtual functions. * Is usually instantiated dynamically on the heap and used via a (smart) pointer.

Traits Classes

Are templates that carry information about types.

Policy Classes

Are fragments of pluggable behavior.

Exception classes

They exhibit an unusual mix of value and reference semantics: They are thrown by value but should be caught by reference.

  • Has a public destructor and no-fail constructors (especially a no-fail copy constructor: throwing from an exception's copy constructor will abort your program!). * Has virtual functions, and often implements cloning and visitation. * Preferably derives virtually from std::exception.

Prefer minimal classes to monolithic classes

Divide and conquer: Small classes are easier to write, get right, test, and use. They are also more likely to be usable in a variety of situations. Prefer such small classes that embody simple concepts instead of kitchen-sink classes that try to implement many and/or complex concepts.

  • A minimal class embodies one concept at the right level of granularity. * A minimal class is easier to comprehend, and more likely to be used and reused.

Prefer composition to inheritance

Avoid inheritance taxes: Inheritance is the second-tightest coupling relationship in C++, second only to friendship. Tight coupling is undesirable and should be avoided where possible. Therefore, prefer composition to inheritance unless you know that the latter truly benefits your design.

  • Avoid inheriting from classes that were not designed to be base classes!

BUT: Do use public inheritance to model substitutability!!

Prefer providing abstract interfaces

Abstract interfaces help you focus on getting an abstraction right without muddling it with implementation or state management details. Prefer to design hierarchies that implement abstract interfaces that model abstract concepts.

  • An abstract interface is an abstract class made up entirely of (pure) virtual functions and having no state (member data) and usually no member function implementations. Note that avoiding state in abstract interfaces simplifies the entire hierarchy design.

Follow the Dependency Inversion Principle (DIP): * High-level modules should not depend upon low-level modules. Rather, both should depend upon abstractions. * Abstractions should not depend upon details. Rather, details should depend upon abstractions. =⇒ Hierarchies should be rooted in abstract classes, not concrete classes. Put another way: Push policy up and implementation down! * Be wary about using multiple inheritance of classes that are not abstract interfaces

Overriding

When overriding a virtual function, preserve substitutability; in particular, observe the function's pre- and post-conditions in the base class. Don't change default arguments of virtual functions. Prefer explicitly redeclaring overrides as virtual.

  • Beware of hiding overloads in the base class. (Redeclaring one version of an overloaded function in a subclass hides all other base-class versions of the overloaded function!) * If the base class's overloads should be visible, write a using declaration to redeclare them in the derived class

Avoid providing implicit conversions

Think twice before providing implicit conversions to and from the types you define, and prefer to rely on explicit conversions (explicit constructors and named conversion functions).

Implicit conversions have two main problems:

  • They can fire in the most unexpected places. * They don't always mesh well with the rest of the language.

Implicitly converting constructors (constructors that can be called with one argument and are not declared explicit) interact poorly with overloading and foster invisible temporary objects that pop up all over. –>

  • By default, write explicit on single-argument constructors

. class Widget {

. explicit Widget( unsigned int widgetizationFactor ); explicit Widget( const char* name, const Widget* other = 0 );

};

  • Use named functions that offer conversions instead of conversion operators

. const char* toCharPointer() const;

Make Data Members private

Keep data members private. Only in the case of simple C-style struct types that aggregate a bunch of values but don't pretend to encapsulate or provide behavior, make all data members public. Avoid mixes of public and nonpublic data, which almost always signal a muddled design.

  • Private data demonstrates that you have invariants and some intent to preserve them * Nonprivate data members are almost always inferior to even simple passthrough get/set functions, which allow for robust versioning. * Mixing public and nonpublic data members in the same class is confusing and inconsistent * Protected data has all the drawbacks of public data, because having protected data still means that an abstraction is sharing the responsibility of maintaining some invariant with an unbounded set of code. In this case, with the unbounded set of current and future derived classes.

Don't give away internals

Avoid returning handles to internal data managed by your class, so clients won't uncontrollably modify state that your object thinks it owns. * If you absolutely _have_ to return references to internal (private) data, at least return const references!

Construction Destruction and Copying

Define and initialize member functions in the same order

Member variables are always initialized in the order they are declared in the class definition; the order in which you write them in the constructor initialization list is ignored. Make sure the constructor code doesn't confusingly specify a different order.

==Prefer initialization to assignment in constructors == In constructors, using initialization instead of assignment to set member variables prevents needless run-time work and takes the same amount of typing.

  • The objects not explicitly initialized by you are automatically initialized before your program gets to the constructor body using their default constructors (if they exist, else: compile error) , and then assigned to using their assignment operators. Most often, the assignment operator of a nontrivial object needs to do slightly more than a constructor because it needs to deal with an already-constructed object.

== Avoid calling virtual functions in constructors and destructors== Virtual functions dont always behave virtually: Inside constructors and destructors, they don't. Worse, any direct or indirect call to an unimplemented pure virtual function from a constructor or destructor results in undefined behavior. If your design wants virtual dispatch into a derived class from a base class constructor or destructor, you need other techniques such as post-constructors.

. 'In C++, a complete object is constructed one base class at a time. Say we have a base class B and a class D derived from B. When constructing a D object, while executing B's constructor, the dynamic type of the object under construction is B. In particular, a call to a virtual function B::Fun will hit B's definition of Fun, regardless of whether D overrides it or not.'

If you really need post-construction its easiest to have your object created by a factory and call the post construction method before returning the object!

Make base class destructors public and virtual, or protected and nonvirtual

If deletion through a pointer to a base Base should be allowed, then Base's destructor must be public and virtual. Otherwise, it should be protected and nonvirtual.

Therefore: Always write a destructor for a base class, because the implicitly generated one is public and nonvirtual!!

Destructors, deallocation, and swap never fail

Never allow an error to be reported from a destructor, a resource deallocation function (e.g., operator delete), or a swap function. Specifically, types whose destructors may throw an exception are flatly forbidden from use with the C++ standard library.

Copying and Destroying Objects

If you define any of the copy constructor, copy assignment operator, or destructor, you might need to define one or both of the others.

Copy Semantics

A common mistake is to forget to think about copy and assignment semantics when defining a class → Ensure that your class provides sensible copying:

  • Explicitly disable both copy construction and copy assignment (by inheriting boost::noncopyable). This should be the default unless you KNOW you will need to make copies of the objects (for example objects without copy constructors can not be put in STL containers directly.. but smart_pointers work). Especially define Base Classes as noncopyable to avoid object slices!
  • Explicitly write both construction and copy assignment functions
  • * If copying makes sense and the default behavior is correct, don't declare them yourself and just let the compiler-generated versions kick in. Prefer to comment that the default behavior is correct!

Assignment Semantics

Be sure when which type of assignment is used. As a guideline: if a variable already exists an assignment uses the operator= method, otherwise a constructor is used.

MyClass x();   //constructor MyClass 
y(x);   //copy constructor 
x = MyClass();  //operator=

Namespaces and Modules

cppbp.txt · Last modified: 2012/07/20 07:58 by mantis