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

Classes meant to be used standalone (utility) obey a different blueprint than base classes (See Item 32: Be clear what kind of class you are writing). Using a standalone (utility) class as a base class is a serious design error and should be avoided. Keep these principles in mind:

  • To add behavior, prefer to add nonmember functions instead of member functions (See Item 44: Prefer writing nonmember nonfriend functions).

  • To add state, prefer composition instead of inheritance (See Item 34: Prefer composition to inheritance).

  • Avoid inheriting from concrete base classes.

Adding behavior

To add behavior, prefer to add nonmember functions instead of member functions. It is easier to build generic methods if they are not part of a class. For example, why is std::find not a member of std::vector? Because then it wouldn’t work for lists, maps, etc.

Example:

class Car
{
    Wheel frontLeft;
    Wheel frontRight;
    Wheel rearLeft;
    Wheel rearRight;
    Wheel spareInTrunk;

public:
    void wheelsOnCar( list< Wheel > &wheels )
    {
        wheels.push_back( frontLeft );
        wheels.push_back( frontRight);
        wheels.push_back( rearLeft);
        wheels.push_back( rearRight);
    }
    const Wheel & getSpare(){ return spareInTrunk; }
    void setSpare( const Wheel &newSpare ){ spareInTrunk = newSpare; }
};

void wheelsRelatedToCar( Car *aCar, list< Wheel > &wheels )
{
    aCar->wheelsOnCar( wheels );
    wheels.push_back( aCar->getSpare() );
}

Adding state

To add state, prefer composition instead of inheritance.

Inheritance:

The Employee “is a” Person or inherits from Person. All inheritance relationships are “is-a” relationships.

using std::string;

class Person
{
    string Title;
    string Name;
    int Age;
}

class Employee : Person
{
    int Salary;
    string Title;
}

Composition:

Composition is typically “has a” or “uses a” relationship. Here the Employee class has a Person. It does not inherit from Person but instead gets the Person object passed to it, which is why it “has a” Person.

using std::string;

class Person
{
    string Title;
    string Name;
    int Age;

    public Person(string title, string name, string age)
    {
        this.Title = title;
        this.Name = name;
        this.Age = age;
    }
}

class Employee
{
    int Salary;
    private Person person;

    public Employee(Person p, int salary)
    {
        this.person = p;
        this.Salary = salary;
    }
}

Person johnny = new Person ("Mr.", "John", 25);
Employee john = new Employee (johnny, 50000);

Composition over Inheritance:

Now say you want to create a Manager type so you end up with:

class Manager : Person, Employee
{
}

This example will work fine, however, what if Person and Employee both declared Title? Should Manager.Title return “Manager of Operations” or “Mr.”? Under composition this ambiguity is better handled:

Class Manager
{
    public Title;
    public Manager(Person p, Employee e)
    {
        this.Title = e.Title;
    }
}

The Manager object is composed as an Employee and a Person. The Title behaviour is taken from employee. This explicit composition removes ambiguity among other things and you’ll encounter fewer bugs.

Avoid inheriting from concrete base classes.

Inheriting from a class with a public non-virtual destructor risks littering your code with undefined behavior by deleting pointers to base class objects that actually point to the derived objects.