Re: C++ Primer 4th edition Reference Counting Smart Pointers
On Jul 1, 8:57 pm, Paavo Helde <pa...@nospam.please.ee> wrote:
James Kanze <james.ka...@gmail.com> kirjutas:
On Jun 30, 11:24 pm, Paavo Helde <pa...@nospam.please.ee> wrote:
James Kanze <james.ka...@gmail.com> kirjutas:
As a general rule, it's probably best to avoid using
reference counted pointers on objects which themselves
contain reference counted pointers.
Can you give some rationale for this claim? Is it just a dead
sure way to avoid cycles? I'm asking because at least 90% of
smartpointers in our applications reside inside of other
smartpointed objects.
Obviously, it is a 100% sure means of avoiding cycles.
More generally, however: what sort of object would be managed by
a reference counted pointer, and would also contain one? There
are probably exceptions, but the only types of objects I can
reasonably see containing reference counted pointers are entity
objects, and you don't want reference counted pointers to entity
objects, since entity objects normally have deterministic
lifespans. If I look at my own code, almost all of the objects
I have which are managed by reference counted pointers are
agents of some sort or another---small polymorphic objects with
no state of their own, which are created and used to do just one
special thing. Any pointers they have are for navigation, and
so are either raw pointers, or if there is some chance that the
lifetime of the object referred to ends before the agent is
finished, some sort of "observer" pointer, which will
automatically be null'ed in the destructor of the pointed to
object.
I see. It appears we are using smartpointers in quite for
different purposes. Most our uses involve complicated data
structures (containers, arrays, tables, images), parts of
which are in shared use for efficiency reasons, and refcounted
for keeping track of the shared usage count. In principle we
could always make deep copies when creating new data
structures and avoid smartpointers altogether, but this would
create excessive overhead (already now in some situations we
are approaching the virtual address space limit, for 32-bit
compilations).
Aha. You're using them to implement CoW. I'll admit that I
hadn't considered that---my pre-standard String class used CoW
(but that was before threading because common), but since then,
I haven't needed it much. But you're right, reference counted
pointers are a good solution here, provided that the structure
is guaranteed to be a tree, or at least an acyclic graph. (Come
to think about it, that's exactly how I manage the parse tree in
my RegularExpression class. I'd sort of forgotten about that,
however, since it's something I wrote around 15 years ago.)
For most cases garbage collection would work for our code as
well. However, smartpointed object lifetime is more
deterministic than in case of garbage-collection (where it is
logically infinite). If all references to the object are gone,
the object is destroyed _in situ_.
Which in the case of arbitrary trees can be very expensive in
terms of runtime. Complicated graphs are the choice example
when one want a benchmark showing the speed of garbage
collection:-).
In some cases this triggers extra actions in our application,
which have to be carried out deterministically. One could
possibly argue that in such cases the ownership issues must be
clear enough anyway (a human must figure it out and be sure in
which timepoint the object is destructed) so that smartpointer
approach would not be needed, but this would require extra
effort and make the data model inhomogeneous, so we are using
smartpointers also for such data objects. Maybe this is a
mistake, making our application more fragile.
It all depends. I've definitely used reference counted pointers
for triggering some specific actions (generally not in
conjunction with memory management). I would have expected that
if you're dealing with trees, etc., about the only pointer which
would have such associated actions would be the root, but I
suppose there could be specific cases of subtrees as well. In
which case, homogeneity would argue for using reference counted
pointers everywhere. Curiously, the only time I needed
something similar was in Java:-)---we defined a dispose function
in the base class, and walked the tree, calling it. Arguably,
this is a "cleaner" solution, since it provides for things like
error reporting, etc. On the other hand, it's a bit more work,
and there are certainly cases where it's clear that errors can't
occur (or would be fatal).
--
James Kanze (GABI Software) email:james.kanze@gmail.com
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