C++ Inheritance is Anti-Pattern

As I am also claiming since ages ago, inheritance as (class or component) composition method is evil. There is no reason to use class inheritance.

And I am not the inventor of this advice. What more can we say?

Subclassing

Code reuse, aka Implementation Inheritance. This is the bad one.

TextDoc is NOT a Parser. But here is this Parser class that was bought in from a third party and I am lazy and I “just inherit” it. So that I can analyze the text as clients do require. If Parser changes TextDoc code recompiles. But who cares. I am already on the next contract. Even worse if Parser  v 1 has a bug nothing changes and nobody notices TextDoc has a bug too. TextDoc and a Parser are tightly coupled.

Subtyping

This is better.  The inheritance of base types aka interfaces for the purpose of their implementation. C++ has not “interface” construct. MSVC has one for you.  Please use it.

MyCounter is an counter as above. Inheritance here has its purpose. It is used to implement the behavior. Behavior is expressed as interface.
final is C++ keyword since C++11 and its introduction is excellent news for people who wish to stop other people abusing inheritance.
So that (for example) lazy contractor from use-case 1 can not abuse our counter:

Usaly mentioned with but actual not tightly coupled to inheritance is:

Polymorphism

A promise of substitutability: ie TextDoc and BinaryDoc are both Document. Thus we can “call them document”. We can generalize the code designs. But pay attention: this is just a promise. Very rarely one can find a pure polymorphic behavior in mature code. That requires two things mostly missing from most projects started in a rush: discipline and vision.

Above we do not inherit for code reuse. Instead we capture the relationship from, the reality into the design and we implement it and use it.  Function opendoc does not care what kind of doc are we opening.

That is polymorphic behaviour. It allows us to use the behavior of base concept (aka base class) while operating on derived concepts (aka derived classes)
Why do we declares and use IDocument inteface? That decouples us from the particular Document internals.  And most importantly that allows for feasible design changes. For example we could decide in the future to be able to incorporate and use on-line documents.

And we do not require users to recompile their legacy code using our library, just because we added yet another type of a document, for example db-hosted.

The code is here.

This code/design also has one more very important quality: Resilience to change. It is highly changeable. Changing this code is easily manageable. From both developers and clients /users point of view.

Fast forward to reality

This is the age of modern C++. Visual Studio 2017, and other wonders.  Also, now there is this MSVC “permissive” compiler switch, and there is this upheaval and debating around it. And the most feverish ones in that debate are the same ones who abused the inheritance in the dark ages of C++.

But they have been using templates. And they have appeared “clever” too. So what exactly is the problem they have with modern C++? It is the introduction of a so called “Two phase name lookup“. Another very good explanation/solution is here.

Just an important note from me on the permissive MSVC compiler switch. If I may. You can use the /permissive- compiler option to specify standards-conforming compiler behavior.

As simple as that. This is not a switch for the conformance to particular C++ version. It is simply here to warn you if you are breaking modern C++  rules.

My summary is this: Why inheritance?

Alas the followers of the religion of inheritance, now need to change their code to conform to modern C++ compilers.

Just because in their youthful and fast past, they used the clever (template) inheritance, each and every base class or call, now has to be changed to make it conform with modern C++.

Now [lease consider this extremely simple code:

I do have following questions:

  1. How is  Derived “better design” vs Aggregated class?
  2. How  is Derived more resilient to change vs Aggregated?
  3. Derived knows about Base member x. Is that good or bad?
  4. Can I add int x ; into Derived? or into Aggregated?

And so on .. I am sure I could add lots more of them questions here.

for the conclusion here is one of Mr Stroustrup’s (simpler) slides:

Stroustrup on inheritance
Stroustrup on inheritance: It has been “systematically overused and misused”

It is (again) as simple as that.