2

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?

  • "My logic here is that foo() and d_map may be used privately across multiple derived classes" - Did you intend to use private inheritance rather than public? – Ben Cottrell Apr 12 '17 at 19:50
  • 1
    Basically, you only want deribed classes to have access to some common functions that they may or may not need. Am I rigth? – Laiv Apr 12 '17 at 20:30
6

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.
  • I am tempted to dispute that static_cast would appear to work in some cases :p -- Joke aside, in what case would a static_cast to a private base class pointer succeed? – Quentin Apr 13 '17 at 8:43
  • @Quentin: Oops--that should have said reinterpret_cast. Thinko on my part. – Jerry Coffin Apr 14 '17 at 3:36
0

It all depends on the business logic of the entities that inherit your base class.

If they all have those functions and fields in common and you don't have to make any special overrides that makes their function different in different inherited classes, then this style is preferable as it is consistent with the L in SOLID, i.e. Liskov principle.

BUT, if the logic of your inherited entities is different or you want to group entities with a common base class, just because they have this two things in common and absolutely nothing else, then a "helper" class is a preferable choice. There is a common quote when breaking Liskov principle.

If it looks like a duck, quacks like a duck, but needs batteries, then you probably have the wrong abstraction.

  • LSP is about writing derived classes which are inter-changable as per the base-class interface, but the question is about base classes with all protected members, so there's no common public interface to speak of, and the LSP doesn't really bring any useful insight. – Ben Cottrell Apr 12 '17 at 20:20
  • @Ben, I agree. LSP is mainly about public methods. But the principle is the same, irrelevant if the methods are protected or public. If I have a lot of inherited classes that practically don't use those protected methods or seldom, then those inherited classes have barely nothing in common with the base class and a "helper" class is a more appropriate solution from engineering perspective. – civan Apr 13 '17 at 6:41
  • 2
    What you're describing is just plain old code reuse through inheritance. LSP isn't about reuse of code, it's concerned with users (clients) of a base class being able to expect uniform behaviour from derived classes. In other words, anywhere which has the base class injected or passed as a method parameter should be able to access its methods without needing to know anything about the concrete derived class being called. – Ben Cottrell Apr 13 '17 at 7:23
0

Well, having publicly accessible data in a class is a bad practice and certaintly not considered a good style.

In any case you should derive privatly from that class. Protected derivation might make sense in some case.

However, usually it would be best to use composition instead. It make that class testable on its own (as the method are then public) and that class can be use somewhere else.

It might also help avoid multiple inheritances if you need that functionnality in a class that already have a base class.

It is always a good idea to have less coupling between classes. Public derivation is a stronger link and should be used only the derived object IS a base object. Protected or private inheritance are less coupled but composition is even better.

0

I cannot tell whether it is good practice or not until I know the semantics of the method in relation to that of the class.

If your base class were Animal and the shared method represents animal behavior like Eat, Talk or Poop, you are good from an OO perspective. If the method however is just some general purpose handy-dandy function like send an email or process a string or print a text representation of the object, you would NOT be good in the OO design department.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.