Why does private inheritance increase the probability, as compared to composition, that someone will break my code?

StackOverflow https://stackoverflow.com/questions/9199482

  •  27-04-2021
  •  | 
  •  

Question

The author of this article states that

"Normally you don't want to have access to the internals of too many other classes, and private inheritance gives you some of this extra power (and responsibility). But private inheritance isn't evil; it's just more expensive to maintain, since it increases the probability that someone will change something that will break your code."

Assume the following code where Car inherits from Engine privately :

#include <iostream>
using namespace std;

class Engine
{
    int numCylinders;
    public:
    class exception{};
    Engine(int i) { if( i < 4 ) throw exception(); numCylinders = i; }
    void start() { cout << "Engine started " << numCylinders << " cylinders" << endl; }
};

class Car : private Engine          
{    
    public:
    Car(int i) : Engine(i) {}
    using Engine::start;
};

int main()
{
    try
    {
        Car c(4);
        c.start();
    }
    catch( Engine::exception& )
    {
        cout << "A Car cannot have less than 4 cylinders" << endl;
    }
}

My question is : how a Car can break this code, by setting up, for example, its Engine with less than 4 cylinders, using private inheritance and without protected members in the base class ?

Was it helpful?

Solution

I think the issue isn't that Car can break your Engine code, but rather that by changing Engine, someone can break your Car code. Inheritance represents a much tighter coupling than composition, so that changes in Engine are more likely to break a class that inherits from it rather than contains it. In the case of C++, an even looser coupling is achieved by having Car contain an Engine pointer or smart pointer.

OTHER TIPS

One way in which inheritance introduces a much tighter coupling than membership is that the name space of the derived and the base class is mixed. Thus the meaning of a name in the context of the derived class depends on which names the base class introduces, and there are the usual overriding/hiding effects. A change in the base class can have effects on code in the derived class that is not necessarily clearly locatable or that would immediately produce a useful diagnostic. By contrast, if the interface of a member object changes, then the most that can break will be code that actually mentions the member object.

I don't see that Car can set Engine::numCylinders (at least not without dirty tricks like accessing the raw memory). The example in the article uses a protected method, you use a private member.

BTW: The article starts with "Use composition when you can" - and a Car has an Engine, but it is not an engine. When A is derived from B then this usually expresses A is a B.

The author of the article, in the previous point, refers to some of the "drawbacks" of using private inheritance, here:

  • The simple-composition variant is needed if you want to contain several Engines per Car
  • The private-inheritance variant can introduce unnecessary multiple inheritance
  • The private-inheritance variant allows members of Car to convert a Car* to an Engine*
  • The private-inheritance variant allows access to the protected members of the base class
  • The private-inheritance variant allows Car to override Engine's virtual functions
  • The private-inheritance variant makes it slightly simpler (20 characters compared to 28 characters) to give Car a start() method that simply calls through to the Engine's start() method
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top