From: Phil Nash (phil.nash.lists_at_[hidden])
Date: 2002-06-26 08:36:17
[Peter Dimov, replying to Phil Nash]
> > > No, you need to construct the counted_base with initial counts of zero
> > > (there is a default constructor present.)
> > Ok... this was how I tried it initially but I had other problems - in
> > particular in the taking weak_ptrs in the constructor of the object
> > ref-counted... maybe that is not possible after all - you didn't answer
> > bit before.
> Yes, I see the problem. In the current implementation, you have to create
> owning pointer to the object first, before any weak pointers, since
> counted_base interprets use_count = 0, weak_count != 0 as an indication
> dispose() has already been called.
> So it seems that you can't create weak_ptr's in the object constructor.
> need a shared_ptr first, and you can't create a temporary shared_ptr to
> 'this' in the constructor since it will call dispose() on block exit.
Yes I see that now (especially since I better understand something you say
later in this posting now).
However the problem seems to boil down to whether you create the object with
an initial ref count of 0 or 1.
a) If you create it with a ref count of 0 you create it un-owned and expect
an intrusive_ptr to take over ownership of it immediately (and maybe
subsequently shared_ptrs and weak_ptr refs to it too).
b) If you create it with a ref count of 1 you create it in a state where it
owns itself (like a COM object), and the last release by any smart pointer
will not delete it.
Case (a) is the most logical and natural in most cases, and appears to be
the intentional usage pattern. However case (a) does not allow you to obtain
weak_ptr references from within the constructor (because no-one owns it
Case (b) does allow weak_ptr's to be taken from 'this' in the constructor
(AFAICS), but suffers from a ref count that is subsequently one more than we
would like it to be, and therefore doesn't automatically get deleted.
Put like this the simple solution to the case where you need weak_ptrs from
the constructor seems to be take case (b) (initial ref count of 1), but call
release() immediately after taking ownership by an intrusive_ptr.
I have tried this out and it appears to be working smoothly in my code so
far. Do you see any problems with this?
One issue is obviously the exception safety of this two stage operation
(take intrusive_ptr then call release() ).
In my code I have wrapped the taking of the intrusive_ptr and the call to
release() up in another class that performs those operations in its
constructor and destructor respectively (avoiding the initialiser list,
though - so an exception thrown during construction of the intrusive_ptr
would invoke the destructor that calls release() ).
Another problem is that you can still accidentally take an initial
intrusive_ptr (or indeed a shared_ptr) straight from the self-owning object
and not call release() - resulting in an insiduous leak :-o
I have addressed this in my code by modifying the Y* constructor of
shared_ptr to STATIC_ASSERT that Y is not derived from boost::counted_base -
and disallowing use of intrusive_ptr directly in our application code (once
the initial intrusive_ptr has been obtained in my MakeShared class we can
use shared_ptrs the rest of the time).
This is, of course, not a good general solution, but it works for us at the
moment. Any suggestions here are welcome :-)
... now onto that other point that I hadn't understood fully before.:
> weak_ptr's are somewhat an oddity in the intrusive case, but they still
> work, in the sense that you can break cyclic dependencies with a weak_ptr.
> cyclic dependency means that your object has shared_ptr members that
> (indirectly) lead back to that same object. You need to reset() these
> shared_ptr members in dispose() to break the cycle.
Aha, yes! Thanks - that's exactly the point I was missing. These things seem
so obvious once they have been pointed out :-) I really was going off down
the wrong track before wasn't I!
> > If so is there a way round this - such as detaching the weak_count into
> > seperate object from use_count?
> Tough question.
Hopefully I have begun to address it with my comments above, although a
fully generalised and well thought out solution still needs a lot of work...
what do you think?
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk