A situation where private inheritance is useful

J

Juha Nieminen

Let me share with you a realization I had about private inheritance.

As we all know, C++ supports three types of inheritance: Public,
private and protected. From these only the public inheritance is a
"true" inheritance from an object-oriented point of view. If you inherit
privately, there is no "is-a" relationship between the derived and the
base classes, and thus private inheritance does not conform to
object-oriented design. So why have this odd private inheritance at all?
Let me present you an example where I found it useful.

Suppose you want to create an intrusive smart pointer which supports
specifying which memory allocator was used to allocate the object being
managed (so that the smart pointer will be able to destroy the object
using that same memory allocator). A naive implementation could look
something like this:

template<typename Obj_t, typename Allocator = std::allocator<Obj_t> >
class IntrusivePtr
{
Obj_t* obj;
Allocator allocator;

public:
// All the necessary public methods here
};

The class has to store the allocator object (which it has to take eg.
as a constructor parameter) because it is possible for allocators to
have an internal state, and if this is so, it must be stored so that the
object can be destroyed appropriately. You cannot simply assume you can
instantiate the allocator whenever it's needed (this could work with
stateless allocators but not with ones with an internal state).

There's an annoying problem with this, though. Most allocators
(including std::allocator) are empty. They do not have any internal
state (ie. they do not have any member variables). If the allocator is
empty, the IntrusivePtr above is reserving completely unused space for
it by keeping an instance as member. The 'allocator' object will take at
least 1 byte (which will then be expanded to the natural word size of
the system for alignment reasons). Thus the size of the IntrusivePtr
class is increased for no good reason: The space is not used for
anything and thus it's a complete waste.

Usually one would want smart pointers (especially intrusive ones) to
be as small as possible. The 'allocator' member effectively doubles the
size of the class, completely uselessly if the allocator is empty.
However, if we want this class to work properly with all possible
allocators, we have to reserve some space for it. However, wouldn't
there be any way of not reserving space for empty allocators?

Well, most C++ compilers implement the so-called empty base class
optimization (allowed by the C++ standard). This means that if we
inherit from an empty base class, the base class will take no space in
the derived class at all. By "abusing" this feature we can make an
improved version of the above class:

template<typename Obj_t, typename Allocator = std::allocator<Obj_t> >
class IntrusivePtr: public Allocator
{
Obj_t* obj;

public:
// All the necessary public methods here
};

Now it's perfect spacewise: If the specified allocator does have an
internal state, it will be stored in the IntrusivePtr class. However,
most importantly, if the allocator is empty, it will take no space at
all (at least with most compilers). The allocator can still be used for
everything that the member could be used by IntrusivePtr.

However, there's a design problem here. By inheriting from Allocator
we are effectively saying that IntrusivePtr is an Allocator. However,
that's not what we meant. We are simply "abusing" inheritance for a
compiler optimization technique, rather than what inheritance is usually
used for in object-oriented programming. This is, basically, a wrong
usage of inheritance. A smart pointer is not an allocator, and shouldn't
be made one. (In this case the proper design is that the smart pointer
*contains* an allocator object, the smart pointer is not an allocator
itself.)

Ok, it's mostly only a cosmetic problem, but it still bothers an
object-oriented purist (like me). Isn't there anything that can be done
about it?

Step in private inheritance:

template<typename Obj_t, typename Allocator = std::allocator<Obj_t> >
class IntrusivePtr: private Allocator
{
Obj_t* obj;

public:
// All the necessary public methods here
};

Now we are inheriting from Allocator, but we are saying that
"IntrusivePtr is *not* an Allocator". We get all the optimization
benefits of the inheritance trick, while still maintaining good
object-oriented design. The IntrusivePtr class can access everything in
Allocator as needed (and if a pointer to the allocator object is needed
for whatever reason, the 'this' pointer can be cast to it), but the
allocator doesn't mess up the public interface of the class.

Basically private inheritance is composition, but getting the
technical benefits of inheritance (in this case empty base class
optimization).

(This is also a good example of where multiple inheritance can be
useful. If, for whatever reason, IntrusivePtr would have to be inherited
from some base class somewhere, we can inherit it both from that base
class and Allocator without problems. This would be a case where the
language supporting multiple inheritance is beneficial.)

Now, if I only could figure out a good example of protected inheritance...
 
V

Victor Bazarov

Juha said:
Let me share with you a realization I had about private inheritance.

As we all know, C++ supports three types of inheritance: Public,
private and protected. From these only the public inheritance is a
"true" inheritance from an object-oriented point of view. If you inherit
privately, there is no "is-a" relationship between the derived and the
base classes, and thus private inheritance does not conform to
object-oriented design. So why have this odd private inheritance at all?
[...]

As soon as one realizes that inheritance is just what it is, and any
access specifier simply limits the area where the relationship between
the derived class and the base class is, well, *accessible*, then
everything with protected inheritance (and private inheritance as well)
becomes kind of clear. I probably wouldn't call it an epiphany, but...

So, the "is-a" relationship *does in fact exist* in all three cases,
only it's "known" /to everybody/ in case of the public inheritance,
/only to descendants, members, and friends/ in case of protected one,
and /only to friends and members/ of the derived class in case of
private inheritance.

Simple as that.

Please don't get me wrong, in no way I am trying to contradict your
findings, except for one statement in the quoted paragraph: 'If you
inherit privately, there is no "is-a" relationship between the derived
and the base classes'.

V
 
M

Marcel Müller

Hi,

Juha said:
Suppose you want to create an intrusive smart pointer which supports
specifying which memory allocator was used to allocate the object being
managed (so that the smart pointer will be able to destroy the object
using that same memory allocator). A naive implementation could look
something like this:

template<typename Obj_t, typename Allocator = std::allocator<Obj_t> >
class IntrusivePtr
{
Obj_t* obj;
Allocator allocator;

public:
// All the necessary public methods here
};

The class has to store the allocator object (which it has to take eg.
as a constructor parameter) because it is possible for allocators to
have an internal state, and if this is so, it must be stored so that the
object can be destroyed appropriately. You cannot simply assume you can
instantiate the allocator whenever it's needed (this could work with
stateless allocators but not with ones with an internal state).

I usually do not implement my smart pointers this way. I usually have an
abstract base class (without virtual functions, but unusable) and I
handle the deallocation in derived classes. This gives you almost all
options. The disadvantage is that the derived classes have to enrich
more than one function of the base class with the deallocation call. But
the performance at runtime is very good. A second advantage is that the
base class or at least any non-trivial part of it can be of non-template
type. This is achieved by storing void* internally. The reinterpret cast
from void* to exactly the same type where it came from is known to be
safe and does not generate any executable code, and all of that is
encapsulated in the type safe template wrapper. This can significantly
decrease the executable size and therefore improve the cache
efficiencies, if the smart pointers are used for many different types.

But of course, the private inheritance is another solution. It only
generates more code.


Marcel
 
J

James Kanze

Juha said:
Let me share with you a realization I had about private
inheritance.
As we all know, C++ supports three types of inheritance:
Public, private and protected. From these only the public
inheritance is a "true" inheritance from an object-oriented
point of view. If you inherit privately, there is no "is-a"
relationship between the derived and the base classes, and
thus private inheritance does not conform to object-oriented
design. So why have this odd private inheritance at all?
[...]
As soon as one realizes that inheritance is just what it is,
and any access specifier simply limits the area where the
relationship between the derived class and the base class is,
well, *accessible*, then everything with protected inheritance
(and private inheritance as well) becomes kind of clear. I
probably wouldn't call it an epiphany, but...
So, the "is-a" relationship *does in fact exist* in all three
cases, only it's "known" /to everybody/ in case of the public
inheritance, /only to descendants, members, and friends/ in
case of protected one, and /only to friends and members/ of
the derived class in case of private inheritance.
Simple as that.

It depends on what you mean by the "is-a" relationship. The
expression "is-a" is usually used to refer to the view client
code has of the object, so it would require public inheritance.

On the other hand, I'd disagree that private inheritance doesn't
conform to object-oriented design. Object-oriented includes a
lot of things. The "traditional" explination of private and
public inheritance is inheritance of implementation and
inheritance of interface. I'd consider both OO, although the
LSP only applies to inhertance of interface.

But IMHO, it can be even more complicated than that. (And this
probably relates to what you said.) Consider a GUI class that
implements the callback interface for GUI events. As far as
clients of the class are concerned, this is an implementation
detail---they don't what to know anything about how the class
handles things like mouse events. So it makes perfect sense for
the inheritance from the callback interface to be private, even
though it's inheritance of an interface.

For a good discussion of use of private and public interfaces,
I'd suggest Barton and Nackman. They make use of both
extensively, using private inheritance systematically whenever
it is inheritance of implementation.
 
R

Rainer Grimm

Hallo,
have a look at the classical adapter pattern.
It's one typical way to implement this pattern.
Here you use multiply inheritance to adjust a old to a new one. You
inherite from the old in a private manner to get the Implemenentation
and from the new to support a new Interface.
An example in english with german comments:
http://de.wikipedia.org/wiki/Adapter_(Entwurfsmuster)

Greetings from Rottenburg
Rainer
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top