Re: C++ Primer 4th edition Reference Counting Smart Pointers
On Jun 29, 8:47 pm, Juha Nieminen <nos...@thanks.invalid> wrote:
Are there hidden problems with the RCSP code in C++ Primer
4th ed that I'm missing?
I have never seen the smart pointer implementation in C++
Primer, but I do agree that creating a good smart pointer
class is a very non-trivial problem. There are all kinds of
safety and usability (and to a lesser extent, efficiency)
concerns which are often hard or laborious to address.
The only real problem is a design one: what problem are you
trying to solve. Reference counted pointers are not a panacea,
and should only be used where appropriate. Once you've defined
that, they aren't that difficult to implement, with one caveat.
Basically small pointers usually attempt to emulate regular
pointers as far as possible, but adding reference counting to
the mix in order to automate object destruction.
That's probably the error. C++ pointers are inherited from C,
and have a lot of "features" which you generally don't want to
However, regardless of all the syntactical tools available in
C++, it's still very hard, if not outright impossible, to make
a class fully emulate a pointer.
And you probably don't want to.
The most obvious case is that a pointer can point to either an
individual object or an array, and deleting is different in
those cases. (Of course one could argue that it's a *good*
thing that smart pointers to objects and smart pointers to
arrays are kept separate and mutually incompatible.)
It is a good thing. I don't think I've ever used array new, in
over 17 years of C++.
Then there's the question of inheritance: A pointer of one
type can point to an object of another, inherited type.
Pointers pointing to different types like this can be assigned
to each other. Moreover, you can cast (statically or
dynamically) a pointer of base class type to a pointer of
derived class type. A smart pointer desiring to emulate
regular pointers would need to address these issues.
Or not. At one time, I implemented support for this in my
reference counted pointer, mainly to prove to myself that it
could be done. In practice, I don't think I've ever actually
used it (except in the unit tests). In practice, casting up and
down a hierarchy is fairly rare, and almost always involves full
entity objects, which have an explicit lifetime, and so
shouldn't be managed by reference counted pointers. (I'm sure
that there are exceptions. But as I said, I've not encountered
one in 17 years of C++.)
A pointer can point to an incomplete type. Making a smart
pointer correctly support incomplete types is possible, but
not glaringly obvious. (Except in the case of intrusive smart
pointers, for which it's just not possible.)
And of course, using anything but an intrusive reference counted
pointer is just folly.
An object might have been allocated with something else than
the default system allocator. Many naive smart pointer
implementations out there don't support deallocation using
that same special allocator. (Also, most don't support
allocating their own ancillary data using a user-specified
allocator, rather than the default system allocator. I think
even the boost smart pointers don't support this.)
And... So there are some objects you can't manage with
reference counted pointers.
Most naive smart pointer implementations are not thread-safe.
Making a smart pointer thread-safe efficiently is extremely
This is the one caveat. Making reference counting thread safe
isn't that difficult, but it does entail a certain overhead.
And usually, the thread safety isn't necessary (so you might not
want the overhead there all the time).
Making a smart pointer safe to use is extremely difficult, if
not impossible. It's basically impossible to make sure that
the same pointer is not given to two different
(non-instrusive) smart pointers. Or that a pointer to
something not allocated with 'new' is not given to it.
I only use intrusive reference counting; anything else IS a
folly. And the fact that a class derives from the required base
class is a fairly strong indicator to the client code that it
must be allocated dynamically. (At one time, at least, I even
had implementation dependent code in the constructor of the
required base class to ensure dynamic allocation. The following
works both under Solaris on a Sparc and under Linux on a PC:
extern int _end ;
int dummy ;
assert( reinterpret_cast< char const* >( this )
> reinterpret_cast< char const* >( &_end )
&& reinterpret_cast< char const* >( this )
< reinterpret_cast< char const* >( &dummy ) ) ;
Intrusive smart pointers have their own gotchas, most of which
naive implementations get wrong. (For example most of them
fail to take into account that objects might be directly
assigned to other objects, which can mess up the reference
counter if not specifically taken into account.)
Objects which are dynamically allocated do not normally support
assignment. But it doesn't matter: the required base class for
the intrusive reference counter declares both the copy
constructor and the assignment operator private; if the derived
class wants to support these operators, it can, but it will be
forced to do the right thing with regards to the base class.
James Kanze (GABI Software) email:firstname.lastname@example.org
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34