Private inheritance means is-implemented-in-terms-of. It is usually inferior to composition, but it makes sense when a derived class needs access to protected base members or needs to redefine inherited virtual functions. For library developers who strive to minimize object sizes, it also offers the ability of empty base optimization.
The behavior of private inheritance is that
- Compilers will generally not convert a derived class object into a base class object
- members inherited from a private base class (protected or public ones) become private members of the derived class
With such behaviors, private inheritance means is-implemented-in-terms-of. Thus, private-inherited derived class D has no conceptual relationship with the base class B - private inheritance is purely an implementation technique in the implementation domain. Using the terms introduced in item 34:
private inheritance means to ignore interface, and only inherit implementation.
Compared to the composition, which also has the meaning of “is-implemented-in-terms-of”, the preference is simple:
use composition whenever we can, and use private inheritance whenever we must.
When must we?
- Primarily when we need to inherit protected members and/or virtual functions
- Sometimes when we want to take advance of empty base optimization (which is the edge case)
Let’s see a typical usecase for private inheritance. Suppose we want to periodically examine the the internal status of class
Widget, and we’d like to reuse the following utility class:
Timer object can be configured to tick with whatever frequency we need, and on each tick, it calls a virtual function, which we can redefine so that it examines the current state of the
Widget object. In order to redefine a virtual function,
Widget must inherit from
Timer. But public inheritance is inappropriate in this case: it’s not true that a
Widget clients should not be able to call
onTick on a
Public inheritance is not a valid option here. Instead, we inherit privately:
After private inheritance,
onTick function becomes private in
Widget, so keep it as private when we redeclare it2.
This is a nice design, but it’s worth noting that private inheritance isn’t strictly necessary - we could use composition instead:
This design is more complicated involving both (public) inheritance and composition, but we do get two more advantages from its complixity:
Widget's derived classes have no access to the private
WidgetTimerdata member, we prevent derived classes from redefining
- We’ve minimized
Widget's compilation dependencies: if
Timer's definition must be available when
Widgetis compiled, so we probably has to
#include Timer.h. If
WidgetTimeris moved out of
Widgetonly contains a pointer to a
WidgetTimer, we only need to simply forward decalare the
#includeanything. Such decouplings can be important for large systems (details in item 31).
The edge case, as the name suggests, is edgy indeed: it applies only when we’re dealing with a class that has no data in it: no non-static data members; no virtual functions (which introduces
vptr to each object, item 7); and no virual base classes (which also incurs a size overhead, item 40). Conceptually, such an empty class should use no space, but C++ requires that freestanding objects must have non-zero size for some technical reasons. That being said,
We’ll find that
sizeof(HoldsAnInt) > sizeof(int), so
Empty data member does require extra memory. With most compilers,
sizeof(Empty) is 1, which is a silently inserted
char inside the
Empty objects to satisfy C++'s requirements. However, alignment requirements (see item 50) may casue compilers to add padding to
HoldsAnInt classes, so the objects of
HoldsAnInt may gain more than just the size of a char - they would actually enlarge enough to hold a second int.
Now comes the point of using private inheritance: rather than containing “freestanding” objects that can not have zero size, we could inherit from
sizeof(HoldsAnInt) == sizeof(int), thanks to empty base optimization (EBO), which is typically supported by most compilersp4.
In practice, “empty” base classes often contain typedefs, enums, static data members, or non-virtual functions, such as those in the STL that contains useful members (usually typedefs), and thus classes for user-defined function objects may inherit from them without worrying about size increase thanks to EBO.
Private inheritance is most likely to be a legitimate design strategy when we’re dealing with two classes not related by is-a where one either needs access to the protected members of another or needs to redefine one or more of its virtual functions. Even in this case, however, a mixture of public inheritance and composition can often yield the expected behavior, albeit with more design complexity.
Most classes aren’t empty, so the EBO is rarely a legitimate justification for private inheritance.
Using private inheritance judiciously means employing it when, having considered all the alternatives, it’s the best way to express the relationship between two classes.
onTickinto the conceptual
Widgetinterface would make it easy for clients to use the
Widgetinterface incorrectly, a clear violation of item 18’s advice to make interfaces easy to use correctly and hard to use incorrectly. ↩︎
onTickin the public side will not change its accessibility, only to mislead clients into thinking they could call it, and this, again, violates item 18’s advice. ↩︎
Widgetinherits directly from
Widget's derived classes may redefine
onTickis private (recall item 35 that derived classes may redefine virtual functions even if they are not permitted to call them). By the way, Java or C# would not have such trouble due to their keyword
It is worth knowing that the EBO is generally viable only under insgle inheritance, and can’t be applied to derived classes taht have more than one base. ↩︎