Re: naked pointer vs boost::shared_ptr<T>
On Mar 9, 7:29 pm, "Peter Dimov" <pdi...@gmail.com> wrote:
On Mar 9, 3:45 pm, "James Kanze" <james.ka...@gmail.com> wrote:
In particular, weak_ptr doesn't imply shared ownership.
But it does imply that I can create a shared_ptr, and thus
obtain shared ownership. A lie, in sum.
There is a concept of temporary (usually scoped) shared ownership that
is often necessary, even when the permanent ownership is exclusive. It
comes handy in situations where the owner can destroy the object
concurrently with the clients being in the middle of operating on it.
I know. This is a fatal error. The problem is that not
destroying the object at the correct time is also an error.
My real objection to shared_ptr here, of course, is the message
it gives; that any one who can get a copy of the shared_ptr can
maintain the object alive as long as he wants. Which is true,
if you use shared_ptr, but it is not what you want, or what the
design calls for.
I have considered using a single shared_ptr, in the object
itself, with all of the "navigation" pointers weak_ptr, but in
addition to the risk of someone holding on to a shared_ptr
(obtained from a weak_ptr) too long:
-- I find setting a pointer to null is NOT the conventional way
in C++ to say I want this object destroyed. The
conventional way to say I want this object destroyed is
"delete objectPtr", and if an object wants to destroy
itself, "delete this". The fact that something like:
pointer_to_self = NULL ;
actually triggers the destruction of the object is a bit too
subtle for my tastes. (If the goal is to say that I want
the object destructed immediately, and I know that
pointer_to_self is the only instance of shared_ptr, of
course. If the intent isn't immediate destruction, but just
to say I'm through with the object, the situation is
different.)
-- I almost always need the observer pattern for other reasons
anyway. Whoever had a pointer to my object will have to
change his strategy to account for the fact that this one
resource is no longer available; generally speaking, I
prefer to handle such changes pro-actively, when the object
is deleted, rather than have to cope with them when I
actually start doing something that would have needed the
object. Even in the simplest case, where it is only a case
of a 1 to n relationship, and nothing else, I prefer
removing the pointer immediately from the std::set, rather
than having to validate and possibly remove pointers when
iterating through it. (And of course, if it is an std::map,
rather than an std::set, and I navigate via a key that I
receive elsewhere, I may never see the invalidated entry, to
remove it. Which will result in a memory leak, and also
slower access times, as the size of the map grows.)
This typically happens when multiple threads are involved, but it's
possible to encounter in single threaded, event driven (and
potentially reentrant) code as well.
I'd say that my argument is even stronger in a multithreaded
context. If I'm going to destruct the object, no other thread
should have a pointer to it, or be doing anything where they
might accidentally try to obtain a pointer to it. If another
object happens to have a (temporary) shared_ptr to the object
when I decide to destruct it, I'm hosed. Deferring the
destructor isn't an option, because it means that the destructor
will no longer run in the thread it should and will no longer be
protected by the locks it needs. A real recepe for disaster.
FWIW: I've used my own invasive shared pointer for over 15 years
now. I still use it today, in multithreaded applications. But
I've not made it "thread safe"; there's no lock around the
incrementation or the decrementation and test. The reason being
that as soon as several threads can access the object, shared
ownership requires additional protection anyway. (There are
doubtlessly exceptions where immutable objects are involved, but
I've not encountered the case.)
This is why weak_ptr only gives you a shared_ptr, to prevent
the owner from destroying the object just after you've
obtained a reference to it.
I understant the rationale. I just don't think it holds up in
actual practice. And it certainly doesn't hold up in this case;
the semantics of the program call for the destruction of the
object, now, and not some indeterminate later.
shared_ptr+weak_ptr is not the only concurrency-resilient solution to
the owner/observer problem, of course. Everyone is welcome to come up
with their own schemes. Watch out for deadlock, though.
My experience is that the lower the granularity of the locking,
the higher the risk of deadlock. And a thread safe shared_ptr
has a very low granularity.
--
James Kanze (Gabi Software) email: james.kanze@gmail.com
Conseils en informatique orient?e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S?mard, 78210 St.-Cyr-l'?cole, France, +33 (0)1 30 23 00 34
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]