This page covers design conventions that you should adhere to. Some of them have proven useful for OO design in general, others are special to C++ applications.

Many tips are taken from the book [http://www.aw-bc.com/catalog/academic/product/0,1144,0321334876,00.html Effective C++] by Scott Meyers.

Inheritance and object oriented design

Make sure (public) inheritance models "is a"

Whenever you derive a class D from a class B, every object of type D is a B in the sense that the derived class objects must be substitutable for the base class objects.

Do not do it as I (Joachim) have seen it in a real-world project: A person has properties, so CPerson inherits form CPersonProperties, which is a specialization of CProperties. This means, each CPerson is a CProperty, that is, each person is a property, which is non-sense at best, a catastrophic design flaw at worst.

Substitutability means means that objects of the derived class must behave in a manner consistent with the promises made in the base class' contract. Class D (and its methods) must require no more and promise no less than class B (and its overridden methods):

http://www.parashift.com/c++-faq-lite/proper-inheritance.html

In general, forget about "generalization" and "specialization". Think in terms of substitutability: In OO, a circle (which has one member for its radius) is not a specialization of an ellipse (which has two), though in mathematics it is one. The question is whether a circle can be put in wherever an ellipse can. (Most probably not, because it requires more: Even if a circle is considered to be an ellipse, it additionally requires that its two radii are the same.)

In the same sense, an ostrich (which cannot fly) is not a bird (if each bird has a method fly() that augments the bird's height over the ground):

http://www.parashift.com/c++-faq-lite/proper-inheritance.html#faq-21.6

Differentiate between inheriting an interface, inheriting an implementation, and inheriting both of them

When should you make a method purely virtual, when non-purely (impurely?) virtual, and when non-virtual?

  1. The purpose of declaring a pure virtual function is to have derived classes inherit a function interface only. (You know too less for an implementation.)

  2. The purpose of declaring a simple (non-pure) virtual function is to have derived classes inherit a function interface as well as a default implemenation. (You can and want to give a default implementation, but let derived classes decide whether to override it.)

  3. The purpose of declaring a non-virtual function is to have derived classes inherit a function interface as well as a mandatory implementation. (You want all derived classes to have exactly the same behavior.)

class Car
{
public:
  virtual void driveToLocation(const Location& loc) = 0; // 1. Depends too much on the concrete car model (this makes the class abstract)
  virtual void hoot() { std::cout<< "Hoooooooot!" << std::endl;}    // 2. Some models may make a noise of their own
  const string getNumberPlate() { return _numberPlate; } // 3. Must be exactly this for all models
private:
  string _numberPlate;
}

Do not make all member functions virtual a priori. Do not make all member functions non-virtual a priori. Differentiate!

Never redefine an inherited non-virtual function

Do not do this:

class B {
  void foo(); // non-virtual, keyword "virtual" missing
};

class D : public class B {
  void foo(); // redefine B::foo()
};

Rationale: Because D is (publicly) derived from B, every D object is a B object. If D really needs to implement foo() differently from B, and if every B object really has to use B's implementation, then D is not a B and so should not be (publicly) derived from B, a contradition!

Never redefine an inherited parameter value

enum Color { RED, GREEN };

class Shape {
public:
  virtual void draw(Color color = RED) = 0;
};

class Rectangle : public Shape {
public:
  virtual void draw(Color color = GREEN); // BAD: parameter value is statically bound
};

// in main.cpp:
Shape* pr = new Rectangle();
pr->draw(); // calls Rectangle::draw( RED ) !!!

Parameter values are statically bound, but virtual functions are dynamically bound. This will shurely confuse you or the client of your code.

Avoid downcasts

Downcasts typically lead to code that finds code: "If the object is of type A then do X, if the object is of type B then do Y". This might become a maintanance nightmare. Use inheritance and virtual functions instead.

Inheritance vs. templates

Use a template to generate a collection of classes when the type of the objects does not affect the behavior of the class's functions: The behavior of stack<int>::pop() is quite the same as the behavior of stack<Car>::pop().

Use inheritance to generate a collection of classes when the type of the objects does affect the behavior of the class's functions: The behavior of Poodle::bark() is different from the behavior of Sheepdog::bark() (which are both implementations of the pure virtual Dog::bark()).

Construction, destruction, and assignment

Make destructors virtual in base classes

Good rule of thumb: Make the destructor virtual if and only if the class contains at least one virtual function.

http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.7

Abide by the Law Of The Big Three

The Big Three are the destructor, the assignment operator and the copy constructor. In general, a class with any of the Big Three needs all of them.

A class with dynamically allocated memory should have the Big Three implemented.