In C++ we have three kind of inheritance: public, protected and private. Within a previous article I have shown the concepts of public inheritance. By using public inheritance, you can implement your software according to the object-oriented paradigm and create an “is-a” relationship between two classes. In public inheritance, the derived class “is-a” base class.
In contrast, private inheritance creates some other kind of relationship. The derived class is no longer a base class as the public base class interface will be changed to a private interface. The following code shows an according example. A function expects a base class as input parameter. You can pass a derived class to this function if you use public inheritance. But if you try the same by using private inheritance, the compiler will show you an according error.
class Base {}; class DerivedPublic : public Base {}; class DerivedPrivate : private Base {}; void DoSomething(Base x) { } int _tmain(int argc, _TCHAR* argv[]) { DerivedPublic derivedPublic; DerivedPrivate derivedPrivate; DoSomething(derivedPublic); // OK, derived is a base DoSomething(derivedPrivate); // Error, derived is not a base return 0; }
“is implemented in terms of”
But why do we need private inheritance and what kind of relationship do we create? Private inheritance is done because the derived class wants to take advantage of the features of base class. There is no conceptual relationship between base and derived at all. So, private inheritance is a pure implementation technique and not a conceptual idea of the object-oriented paradigm.
In private inheritance, the implementation of the base class will be inherited and the public interfaces should be ignored. As the base class functions become private in derived class, these features are no longer important for a client and become just implementation detail. So, the derived class „is implemented in terms of“ the base class, nothing more.
Another short term to describe the relationship is: “has-a”. A derived class “has-a” base class and uses their features. If you prefer the shorter “has-a” term you should keep it in mind. I like the longer description “is implemented in terms of”, as it emphasizes the key point: private inheritance is a technical concept.
Private inheritance vs. composition
So far, we didn’t answer the question why we need private inheritance, or if we need it at all. Before I try to answer this question, I want to show an alternative to private inheritance. There is another common software design which creates a “has-a” relationship: composition. You can implement a class and use the features of another class by creating a member instance. The following example shows the two concepts.
class Engine {}; class Car1 : private Engine {}; class Car2 { private: Engine mEngine; }; int _tmain(int argc, _TCHAR* argv[]) { return 0; }
We want to implement a car class. A car “has-a” engine but there is no “is-a” relationship between these two classes. So, we should not use public inheritance. But private inheritance is fine, as well as composition. As we have two ways to implement the same thing we must think about the differences to identify the right concept for our use case.
In object-oriented software, you write classes and you create relationships between the classes. Private inheritance as well as composition are ways to create such relationships. In object-oriented software, you can increase the code quality, if you reduce the relationships between classes. The software complexity grows with the number and the strength of relationships.
Private inheritance as well as composition creating one relationship. So, there is no difference in the number of connections. But the two concepts differ in the strength of their binding. Inheritance is the strongest kind of relationship between two objects. Composition instead is a looser form of binding. If you combine composition with the use of interfaces, you can define the class member as instance of some class which implements the interface. This interface based kind of composition is the loosest kind of relationship between objects. In summary, you should prefer composition over private inheritance, as composition will always create a looser coupling of objects.
Use cases for private inheritance
We should always prefer composition over private inheritance. So, we don’t need private inheritance any longer? Of course, it isn’t that easy. Each rule has their exceptions. We will find some (rare) cases were technical limitations will make it not possible to implement a class composition. Following, I want to show some of these most common use cases for private inheritance.
Use case: virtual functions
I think the most common use case for private inheritance is the need to overwrite virtual functions of some tool class. Let me explain this use case based on an example. Let’s say you want to implement an own button control class. So, amongst other things, you have to deal with mouse events. Within your existing application framework, you will find a mouse-event class. The following source code shows the class interface. To keep it simple I omitted the event parameters, like the mouse movement coordinates.
class MouseEvent{ public: MouseEvent(); virtual void OnMouseMove() const; // automatically called virtual void OnLeftButtonDown() const; // automatically called virtual void OnLeftButtonUp() const; // automatically called virtual void OnRightButtonDown() const; // automatically called };
The MouseMove class offers several virtual functions, which will be called on the according mouse event. You can overwrite these functions to handle the events. So, this class offers the functionality you need for your button class. As you start your implementation you think about the possible relationship for the button and the mouse event class. A button is not a mouse event. Therefore, you don’t have an inheritance relationship, in terms of the object-oriented paradigm. A button “has-a” mouse event. This kind of relationship should be implemented by using composition. But in this case it isn’t possible because you have to overwrite the virtual functions. This technical limitation makes it necessary to use the other possible implementation for a “has-a” or “is implemented in terms of” relationship: private inheritance.
class MouseEvent{ public: MouseEvent(); virtual void OnMouseMove() const; // automatically called virtual void OnLeftButtonDown() const; // automatically called virtual void OnLeftButtonUp() const; // automatically called virtual void OnRightButtonDown() const; // automatically called }; class MyButton : private MouseEvent { private: void OnLeftButtonDown() const override { // ... } };
As you can see, private inheritance will offer a technical concept to deal with such situation. Even if it is not preferred to implement a “has-a” relationship by using inheritance, it is the only possible solution in this case.
There is another implementation pattern for such use cases. This pattern is also based on inheritance but this time we use a combination of public inheritance and composition. The resulting solution has a little more difficult design as one additional class is involved. But you will get the benefits of decoupling and therefore cleaner code with minimized compilation dependencies, which may become an important issue in large systems.
class MouseEvent{ public: MouseEvent(); virtual void OnMouseMove() const; // automatically called virtual void OnLeftButtonDown() const; // automatically called virtual void OnLeftButtonUp() const; // automatically called virtual void OnRightButtonDown() const; // automatically called }; class ButtonMouseEvent: public MouseEvent { public: //...here you may offer methods to consume the mouse events // (e.g. callback or observer registration) private: void OnLeftButtonDown() const { //... } }; class MyButton { private: ButtonMouseEvent* pMouseEvent; };
Nevertheless, the fact that there are several solutions to handle such use cases, we should try to prevent such a bad software design. In this use case, the design of the MouseEvent class was the cause of the issue. This class forces a client to overwrite functions. Therefore, it generates a technical limitation which inevitable leads to inheritance. So, the class designer forces a client to use the strictest kind of relationship between objects. This will result in the associated disadvantages and makes the resulting source code less changeable, expandable and maintainable.
Use case: Access protected members
In case a class needs access to protected members of another class, you cannot use composition too, as it is limited to the public interface. In such a case, you also have to use inheritance. For example, for our button class, we want to use some other already existing functionality. This functionality is implemented in a protected member function within an existing class in our framework. In this case we also have a “has-a” relationship but we cannot implement it by using composition. Again, because of technical limitations, we have to use private inheritance or as alternative a combination of public inheritance and composition. Therefore, from a software design point of view, this use case is to the previous one.
Use case: Empty base class optimization
There is another use case, known as “empty base class optimization”. Private inheritance is used to allow empty classes which do not need any memory. This use case is only interesting for library developers who wants to minimize object sizes. Therefore, I don’t want to go into detail here. If you are interested in this topic you will find a lot of nice articles out there. Search for “empty base class optimization”, “EBC” or “EBCO” in this case.
Use case: In terms of lifetime management, the tool class should enclose the client class
This sounds strange but if we look at an example it will become easy to understand. Let’s say you want to offer a tool class which locks an object during its lifetime. In this case your locking class should enclose the client which uses the locker. That means, the locker must be created right before the client and it must be destroyed after the client. C++ offers this lifetime management as standard build in feature in inheritance situations. A base class is always created right before the derived class and destroyed after derived class. Therefore, in such a case, inheritance can be used to manage the lifetime of the objects. Your locker class will be a private base class for the derived class which shall be locked.
Of course, this kind of implementation is still based on technical decisions. From a software design point of view, you will find some other possible solutions. For example, a factory class which manages the creation and therefore the lifetime of object instances.
Summary
Private inheritance creates a “is implemented in terms of” relationship between classes, also known as “has-a” relationship. By using composition, you can create the same kind of relationship but with a much looser coupling between the classes. Therefore, you should prefer composition and use private inheritance only of you must. This “must” is based on technical limitations in some rare use cases.
Pingback: Public vs. protected vs. private inheritance | coders corner