Object-oriented – Abstract base class with only protected members

cdesign-patternsinheritanceobject-oriented

Often, I'll abstract common logic out of a class by creating an abstract base class with only protected members. For example:

class Base {

protected:

    void foo() { ... }
    std::map<KeyType, ValueType> d_map;

};

class Derived : public Foo {

public:

    Derived() : Base() { ... }

    // ...

};

My logic here is that foo() and d_map may be used privately across multiple derived classes, so this abstraction factors them into a common base representation.

Question: Does this pattern make sense (and is it considered good style) from a strict design perspective? Would it be preferred to create a separate class with the protected functionality as publicly-accessible members and reference those from derived sub-classes?

Best Answer

If you're just using the base class as a way of sharing implementation between the two (or more) derived classes, and the base class doesn't represent an interface, then public inheritance is almost certainly a mistake.

One possibility would be to use aggregation (put the shared data into a helper class, and include an instance of that class in each "derived" class that needs it).

Another possibility would be to use private inheritance. Private inheritance is usually described as "is implemented in terms of". Where public inheritance needs to conform to the Liskov Substitution Principle (at least in a decent design), private inheritance carries no such requirement. With private inheritance, the derived class is 'aware' of its relationship to the base class, and can use implementation from the base class. That relationship is "hidden" from the outside world though, so there's not an implicit conversion from the derived class to the base class like there is with public inheritance.

class base { };

class D1 : public base { };

class D2 : base { };

int main() { 
    base *b = new D1; // no problem
    base *b2 = new D2; // won't compile
}

If you really insist, you can convert a pointer to the derived to a pointer to base--but you have to use a cast (and, interestingly, it has to be a C-style cast; none of the "new" C++ casts can really do this correctly1).


  1. For those who are tempted to dispute this: yes, a reinterpret_cast will appear to do the job correctly for some cases--but (to give only one counterexample) if you use multiple inheritance, it will only convert from derived to one of the base types correctly. Attempting to convert to another base type will produce bad results.