Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

naked pointer vs boost::shared_ptr<T>

68 views
Skip to first unread message

Sushrut Sardeshmukh

unread,
Feb 28, 2007, 4:24:06 AM2/28/07
to
Should we stop using naked pointer and replace all of them with
boost:shared_ptr or ( shared_array<T> or scoped_ptr) ?

I am trying to come up with C to C++ conversion guidelines for a large
transformation project. Your inputs will be very valuable.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Al

unread,
Feb 28, 2007, 6:47:16 AM2/28/07
to
Hi,

Sushrut Sardeshmukh wrote:
> Should we stop using naked pointer and replace all of them with
> boost:shared_ptr or ( shared_array<T> or scoped_ptr) ?
>
> I am trying to come up with C to C++ conversion guidelines for a large
> transformation project. Your inputs will be very valuable.

If you're going to use one of the typical ones, why not std::auto_ptr?
shared_ptr has semantics which are often inconvenient / incorrect.

Alternatively, I've been experimenting with changing some existing raw
pointers into either:

CheckedPointer<T> or
OptionalPointer<T>

Where the former requires (and asserts) that its pointer never be null,
and the latter allows a null pointer, but asserts that a null pointer is
never dereferenced -- any feedback on this idea is appreciated :).

If you're interested in the code, let me know.

Thanks,
-Al.

peter koch larsen

unread,
Feb 28, 2007, 6:48:24 AM2/28/07
to
On Feb 28, 10:24 am, "Sushrut Sardeshmukh" <bestbr...@gmail.com>
wrote:

> Should we stop using naked pointer and replace all of them with
> boost:shared_ptr or ( shared_array<T> or scoped_ptr) ?

The problem is not so much pointers. My recommendation is that you
should avoid any "delete" or "delete []" in your code. delete should
be used only in classes whos only purpose is to maintain the memory
resource. That class could be std::auto_ptr, boost::shared_ptr or
boost::scoped_ptr. It might even be your own smart pointer (and we all
have some, don't we?).

>
> I am trying to come up with C to C++ conversion guidelines for a large
> transformation project. Your inputs will be very valuable.

RAII (of which the above is an example) is THE paradigm to follow.
Also read some of the nice books by Sutter and Myers (and other gurus
- but these two gurus have written some quite good guides to C++
programming that you should know).

/Peter

Ulrich Eckhardt

unread,
Feb 28, 2007, 6:43:13 AM2/28/07
to
Sushrut Sardeshmukh wrote:
> Should we stop using naked pointer and replace all of them with
> boost:shared_ptr or ( shared_array<T> or scoped_ptr) ?

... or boost::intrusive_ptr or std::auto_ptr or something self-made? Also,
don't forget boost::weak_ptr or boost::optional or boost::any. Generally,
I'd say that doing resource management via new/delete or malloc/free and
with raw pointers is error prone and thus raw pointers are best avoided. In
C++, you rather use references anyway, except when a pointer can reasonably
be null.

> I am trying to come up with C to C++ conversion guidelines for a large
> transformation project.

Show some examples of C use of raw pointers and I might be able give you a
suitable C++ replacement. Otherwise, there are too many uses of pointers in
C that have different equivalents in C++.

Uli

--
Sator Laser GmbH
Geschäftsführer: Ronald Boers Steuernummer: 02/858/00757
Amtsgericht Hamburg HR B62 932 USt-Id.Nr.: DE183047360

Daniel Krügler

unread,
Feb 28, 2007, 3:12:09 PM2/28/07
to
On 28 Feb., 12:47, Al <t...@haik.us> wrote:
> Alternatively, I've been experimenting with changing some existing raw
> pointers into either:
>
> CheckedPointer<T> or
> OptionalPointer<T>
>
> Where the former requires (and asserts) that its pointer never be null,
> and the latter allows a null pointer, but asserts that a null pointer is
> never dereferenced -- any feedback on this idea is appreciated :).

IMO, the name CheckedPointer is not very meaningful, especially
in contrast to OptionalPointer (which says more explicitely what
it wants). What about ValuePointer?
One might also consider to seperate the concerns of holding and
preconditions by means of a proper policy. I remember some very
advanced ansatz (I would say it was from Andrei Alexandrescu),
that distinguished three poly domains.

> If you're interested in the code, let me know.

Yes, would be nice to see.

Greetings from Bremen,

Daniel Krügler

W. J. La Cholter

unread,
Feb 28, 2007, 5:39:01 PM2/28/07
to
"Sushrut Sardeshmukh" <best...@gmail.com> wrote in
news:1172649826.7...@z35g2000cwz.googlegroups.com:

> Should we stop using naked pointer and replace all of them with
> boost:shared_ptr or ( shared_array<T> or scoped_ptr) ?
>
> I am trying to come up with C to C++ conversion guidelines for a large
> transformation project. Your inputs will be very valuable.

As the other posters have suggested, it really depends. It's good to
start with avoiding delete as much as possible. Then, it depends on
the circumstances, and you might want to come up with general
guidelines:
- when a dynamic allocation is necessary and you want to hold it
inside a stack context, auto_ptr is good
- when you want to share with existing containers, shared_ptr
- the Boost pointer containers might be good (just noticed them the
other day but can't claim any practical knowledge)

The important thing is that each has its trade-offs. auto_ptr is
unsuitable for containers; boost::shared_ptr has overhead in
maintaining dynamic reference count. It has been suggested that it
could be good to do a cyclic sharing scheme, especially wrt CPU caches
(Mark Borgerding mentioned that). It would help to look at how
pointers planned to be used rather than come up with a single
rule--that's the problem with guidelines is that they can be too rigid
to account for solid engineering pracitice.

As another example, I worked on a project that purported to require C
and C++. In order to support C code, I couldn't directly use
shared_ptr, so I implemented a custom allocator and wrapper class.
The allocator maintained the reference count with the memory. The C
code had versions of malloc() and free() that handled the
reference-counting. The C++ wrapper class used those functions.

As another poster mentioned, RAII is where it's at. Smart pointers
are part of the equation. Other resources like file handles and
database connections should be wrapped in RAII-friendly classes.

You might find it useful to create a generic template that can be
specialized for different handle types. Or at least document how to
write one.

James Kanze

unread,
Mar 1, 2007, 5:10:24 AM3/1/07
to
On Feb 28, 12:48 pm, "peter koch larsen" <peter.koch.lar...@gmail.com>
wrote:

> On Feb 28, 10:24 am, "Sushrut Sardeshmukh" <bestbr...@gmail.com>
> wrote:

> > Should we stop using naked pointer and replace all of them with
> > boost:shared_ptr or ( shared_array<T> or scoped_ptr) ?

> The problem is not so much pointers. My recommendation is that you
> should avoid any "delete" or "delete []" in your code.

So how do you delete an object with dynamic lifetime? I agree
with regards to delete[], and I don't think I've used it once in
over 15 years of C++. But delete?

Maybe you meant that all delete should be "delete this"?

> delete should be used only in classes whos only purpose is to
> maintain the memory resource.

If the only problem is the memory resource, the Boehm collector
does the job a lot better. Some classes have explicit
lifetimes, and the idiomatic way to terminate the lifetime of
such an object is delete.

> > I am trying to come up with C to C++ conversion guidelines for a large
> > transformation project. Your inputs will be very valuable.

> RAII (of which the above is an example) is THE paradigm to follow.

I'm not sure I follow. RAII obeys scope; if the object lifetime
obeys scope, what are you doing allocating it dynamically to
begin with? There are exceptions, of course, but a lot of the
time, rather than replacing a raw pointer with a smart pointer,
it would be better to do away with the pointer completely.

In my own applications, I find that most pointers are to objects
with explicit lifetimes, and I've yet to find a smart pointer
which is applicable to them (although I've tried). About the
only other cases are polymorphic agents (which are best
collected), and singletons (where dynamical allocation is used
expressedly so that they will never be deleted). In the case of
polymorphic agents, if I can't use the Boehm collector, I will
use either auto_ptr or a shared_ptr of some type.

--
James Kanze (GABI Software) email:james...@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

James Kanze

unread,
Mar 1, 2007, 5:10:12 AM3/1/07
to
On Feb 28, 12:47 pm, Al <t...@haik.us> wrote:

> Sushrut Sardeshmukh wrote:
> > Should we stop using naked pointer and replace all of them with
> > boost:shared_ptr or ( shared_array<T> or scoped_ptr) ?

> > I am trying to come up with C to C++ conversion guidelines for a large
> > transformation project. Your inputs will be very valuable.

> If you're going to use one of the typical ones, why not std::auto_ptr?
> shared_ptr has semantics which are often inconvenient / incorrect.

As does auto_ptr, or any other smart pointer. In new code, I
generally use auto_ptr at threading interfaces (so that passing
the pointer to a different thread makes the object inaccessible
in the original thread), and not much else; I use the Boehm
collector for memory management. In legacy applications, where
I can't always use the Boehm collector, I'll use a mixture of my
own (invasive) reference counted pointer, something along the
lines of scoped_ptr, and auto_ptr, depending on the context and
the semantics I need. (My legacy applications all have to be
compiled with older compilers, so Boost is out. But I think I'd
still go with my invasive reference counted pointer, as being
somewhat less fragile than boost::shared_ptr.)

> Alternatively, I've been experimenting with changing some existing raw
> pointers into either:

> CheckedPointer<T> or
> OptionalPointer<T>

> Where the former requires (and asserts) that its pointer never be null,
> and the latter allows a null pointer, but asserts that a null pointer is
> never dereferenced -- any feedback on this idea is appreciated :).

I don't see much advantage in the second; all of the systems I
work on fail when a null pointer is dereferenced anyway. The
former might be interesting in some contexts, although the name
is a little vague; maybe NonNullPointer<T>?

--
James Kanze (GABI Software) email:james...@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

--

peter koch larsen

unread,
Mar 1, 2007, 10:02:44 AM3/1/07
to
On 1 Mar., 11:10, "James Kanze" <james.ka...@gmail.com> wrote:
> On Feb 28, 12:48 pm, "peter koch larsen" <peter.koch.lar...@gmail.com>
> wrote:
>
> > On Feb 28, 10:24 am, "Sushrut Sardeshmukh" <bestbr...@gmail.com>
> > wrote:
> > > Should we stop using naked pointer and replace all of them with
> > > boost:shared_ptr or ( shared_array<T> or scoped_ptr) ?
> > The problem is not so much pointers. My recommendation is that you
> > should avoid any "delete" or "delete []" in your code.
>
> So how do you delete an object with dynamic lifetime? I agree
> with regards to delete[], and I don't think I've used it once in
> over 15 years of C++. But delete?
>
> Maybe you meant that all delete should be "delete this"?
No. I've rarely had a need for "delete this". I wonder where you find
this idiom useful? About thte only places I've found use for it is in
"free-floating" threads and some rare GUI application.

>
> > delete should be used only in classes whos only purpose is to
> > maintain the memory resource.
>
> If the only problem is the memory resource, the Boehm collector
> does the job a lot better. Some classes have explicit
> lifetimes, and the idiomatic way to terminate the lifetime of
> such an object is delete.
The Boehm collector might not be available, and if it is it might not
be appropriate if it has non-memory resources.

>
> > > I am trying to come up with C to C++ conversion guidelines for a large
> > > transformation project. Your inputs will be very valuable.
> > RAII (of which the above is an example) is THE paradigm to follow.
>
> I'm not sure I follow. RAII obeys scope; if the object lifetime
> obeys scope, what are you doing allocating it dynamically to
> begin with? There are exceptions, of course, but a lot of the
> time, rather than replacing a raw pointer with a smart pointer,
> it would be better to do away with the pointer completely.

If you can get rid of the pointer, you should. But there are lots of
cases where you can't. One such case is when a factory function
returns a polymorphic object or an object that can't be copied. Such
objects would have to be dynamically allocated and returned via a
pointer, and it is in cases such as this that I advocate the pointer
to be intelligent.


>
> In my own applications, I find that most pointers are to objects
> with explicit lifetimes, and I've yet to find a smart pointer
> which is applicable to them (although I've tried). About the
> only other cases are polymorphic agents (which are best
> collected), and singletons (where dynamical allocation is used
> expressedly so that they will never be deleted). In the case of
> polymorphic agents, if I can't use the Boehm collector, I will
> use either auto_ptr or a shared_ptr of some type.

There are also cases where the underlying representation is in a
pointer - COM is an obvious example for Windows guys. I do not share
your experience with being unable to find a reasonable pointer-type.
boost::scoped_ptr or std::auto_ptr is often a fine choice, and if not
you should be able to simply use boost::shared_ptr.

/Peter

Dejan.M...@gmail.com

unread,
Mar 1, 2007, 10:33:31 PM3/1/07
to
On Mar 1, 5:10 am, "James Kanze" <james.ka...@gmail.com> wrote:

> In my own applications, I find that most pointers are to objects
> with explicit lifetimes, and I've yet to find a smart pointer
> which is applicable to them (although I've tried).

Isn't any smart pointer with a reset() method applicable? The memory
owner can declare a smart instead of a naked pointer, and call reset()
instead of delete. Everything else would remain the same. The
advantage would be exception safety and a clear convention for who
owns the memory at any given time.

James Kanze

unread,
Mar 2, 2007, 6:29:33 AM3/2/07
to
On Mar 1, 4:02 pm, "peter koch larsen" <peter.koch.lar...@gmail.com>

wrote:
> On 1 Mar., 11:10, "James Kanze" <james.ka...@gmail.com> wrote:> On Feb 28, 12:48 pm, "peter koch larsen" <peter.koch.lar...@gmail.com>
> > wrote:

> > > On Feb 28, 10:24 am, "Sushrut Sardeshmukh" <bestbr...@gmail.com>
> > > wrote:
> > > > Should we stop using naked pointer and replace all of them with
> > > > boost:shared_ptr or ( shared_array<T> or scoped_ptr) ?
> > > The problem is not so much pointers. My recommendation is that you
> > > should avoid any "delete" or "delete []" in your code.

> > So how do you delete an object with dynamic lifetime? I agree
> > with regards to delete[], and I don't think I've used it once in
> > over 15 years of C++. But delete?

> > Maybe you meant that all delete should be "delete this"?

> No. I've rarely had a need for "delete this". I wonder where you find
> this idiom useful?

Whenever I have objects which have arbitrary lifetimes, and
decide the lifetime themselves. This is almost always the case
of "modeling" objects, for example, which represent some
external entity.

In practice, one or two employers have used relationship
management schemes in which the object told the relationship
maanager to delete it, so there wasn't actually a "delete this"
in the code. But conceptually, it comes to the same thing: the
object decides when its lifetime has ended.

> > > > I am trying to come up with C to C++ conversion guidelines for a large
> > > > transformation project. Your inputs will be very valuable.
> > > RAII (of which the above is an example) is THE paradigm to follow.

> > I'm not sure I follow. RAII obeys scope; if the object lifetime
> > obeys scope, what are you doing allocating it dynamically to
> > begin with? There are exceptions, of course, but a lot of the
> > time, rather than replacing a raw pointer with a smart pointer,
> > it would be better to do away with the pointer completely.

> If you can get rid of the pointer, you should. But there are lots of
> cases where you can't. One such case is when a factory function
> returns a polymorphic object or an object that can't be copied.

That's a good example, and auto_ptr (if one wants to stick with
the standard) or scoped_ptr (if one wants the exact semantics)
can be used to advantage in such cases, if the object has scoped
lifetime. If the object doesn't have scoped lifetime, and the
Boehm collector isn't appropriate, then it may be possible to
use shared_ptr. (I think I mentionned polymorphic agents as one
case where reference counting might be an appropriate solution
in another posting.) I find that such objects generally only
represent a very small percentage of objects in the application,
however. In my applications, most objects are either value
types (which aren't allocated dynamically, so the question
doesn't occur) or entity objects (which have explicit
lifetimes). Roughly speaking (and the variance is high), I'd
say about 60% values, 30% entity objects, and 10% others. Smart
pointers, at least the classical smart pointers, are only
applicable to that last category.

> Such objects would have to be dynamically allocated and
> returned via a pointer, and it is in cases such as this that I
> advocate the pointer to be intelligent.

Agreed, if for some reason using the Boehm collector is not
appropriate. I'm not against using a smart pointer when the
case has been analysed, and a smart pointer is appropriate. I
am against a knee jerk reaction: replace all pointers with
shared_ptr, or whatever.

> > In my own applications, I find that most pointers are to objects
> > with explicit lifetimes, and I've yet to find a smart pointer
> > which is applicable to them (although I've tried). About the
> > only other cases are polymorphic agents (which are best
> > collected), and singletons (where dynamical allocation is used
> > expressedly so that they will never be deleted). In the case of
> > polymorphic agents, if I can't use the Boehm collector, I will
> > use either auto_ptr or a shared_ptr of some type.

> There are also cases where the underlying representation is in a
> pointer - COM is an obvious example for Windows guys.

When dealing with an external interface, obviously, you have to
follow its rules.

> I do not share
> your experience with being unable to find a reasonable pointer-type.
> boost::scoped_ptr or std::auto_ptr is often a fine choice, and if not
> you should be able to simply use boost::shared_ptr.

When it's appropriate. I rarely find that to be the case,
however. (Maybe COM is a good example where it would be the
case. I don't know; I generally work on Unix platforms, and
have never seen or used COM.)

FWIW: historically (before I started using the Boehm collector),
my factories generally did return objects derived from
RefCntObj, the pointed to for my invasive RefCntPtr. (I still
prefer invasive reference counting in such cases, because of the
lower overhead. But if I didn't already have the class, and
could use Boost, I'd use shared_ptr anyway.)

--
James Kanze (GABI Software) email:james...@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

--

Alan Johnson

unread,
Mar 2, 2007, 6:31:52 AM3/2/07
to
Al wrote:
> Hi,
>
> Sushrut Sardeshmukh wrote:
>> Should we stop using naked pointer and replace all of them with
>> boost:shared_ptr or ( shared_array<T> or scoped_ptr) ?
>>
>> I am trying to come up with C to C++ conversion guidelines for a large
>> transformation project. Your inputs will be very valuable.
>
> If you're going to use one of the typical ones, why not std::auto_ptr?
> shared_ptr has semantics which are often inconvenient / incorrect.
>
> Alternatively, I've been experimenting with changing some existing raw
> pointers into either:
>
> CheckedPointer<T> or
> OptionalPointer<T>
>
> Where the former requires (and asserts) that its pointer never be null,
> and the latter allows a null pointer, but asserts that a null pointer is
> never dereferenced -- any feedback on this idea is appreciated :).
>
> If you're interested in the code, let me know.
>
> Thanks,
> -Al.
>

The OptionalPointer seems like a reasonable idea. I would personally
have a hard time finding a use for CheckedPointer.

In the majority of cases I would think your CheckedPointer should just
be a reference. In cases where you need it to also be assignable, it
seems to just duplicate boost::reference_wrapper<T>.

--
Alan Johnson

James Kanze

unread,
Mar 2, 2007, 6:31:40 AM3/2/07
to
On Mar 2, 4:33 am, "Dejan.Mircev...@gmail.com"

<Dejan.Mircev...@gmail.com> wrote:
> On Mar 1, 5:10 am, "James Kanze" <james.ka...@gmail.com> wrote:

> > In my own applications, I find that most pointers are to objects
> > with explicit lifetimes, and I've yet to find a smart pointer
> > which is applicable to them (although I've tried).

> Isn't any smart pointer with a reset() method applicable? The memory
> owner can declare a smart instead of a naked pointer, and call reset()
> instead of delete. Everything else would remain the same. The
> advantage would be exception safety and a clear convention for who
> owns the memory at any given time.

But what does that buy you over a raw pointer?

A fairly large percent of the time, such entity objects will be
referenced through some sort of map; they are known outside the
program, and of course, pointers don't work outside the program,
so they have some other identity key as well. In such cases,
one possible solution would be an
std::map< ExternalKey, boost::shared_ptr< ObjectType > >
To delete the object, the user (or the object itself) removes
itself from the map.

I find this solution a bit too subtle for my tastes. If the
object wants to delete itself, I find the intent far clearer if
the code says "delete this", rather than "registry.erase(
myId )" (statement which is then part of the destructor); it
seems to express the intent better: the object is being
destructed (and thus removed from the registry), rather than the
object is being removed from the registry (which incidentally
means that it's lifetime has ended). Note too that if you use
the registry.erase() method, the object might not be destructed
immediately; someone might still be using it somewhere, and hold
a shared_ptr to it as well. You need deterministic destruction,
and you aren't getting it.

--
James Kanze (GABI Software) email:james...@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

--

Emmanuel Deloget

unread,
Mar 2, 2007, 9:19:27 AM3/2/07
to
On 28 fév, 12:47, Al <t...@haik.us> wrote:
> Hi,
>
> Sushrut Sardeshmukh wrote:
> > Should we stop using naked pointer and replace all of them with
> > boost:shared_ptr or ( shared_array<T> or scoped_ptr) ?
>
> > I am trying to come up with C to C++ conversion guidelines for a large
> > transformation project. Your inputs will be very valuable.
>
> If you're going to use one of the typical ones, why not std::auto_ptr?
> shared_ptr has semantics which are often inconvenient / incorrect.

Are you sure you didn't inverted something? To me, it seems that the
semantics of share_ptr (CopyConstructible, Assignable) are more useful
than the TransferOwnership semantic of auto_ptr, which prohibits its
use in many cases.

Or did I miss something about shared_ptr?

-- Emmanuel Deloget

Dejan.M...@gmail.com

unread,
Mar 2, 2007, 2:15:01 PM3/2/07
to
On Mar 2, 6:31 am, "James Kanze" <james.ka...@gmail.com> wrote:
> On Mar 2, 4:33 am, "Dejan.Mircev...@gmail.com"
>
> <Dejan.Mircev...@gmail.com> wrote:
> > On Mar 1, 5:10 am, "James Kanze" <james.ka...@gmail.com> wrote:
> > > In my own applications, I find that most pointers are to objects
> > > with explicit lifetimes, and I've yet to find a smart pointer
> > > which is applicable to them (although I've tried).
> > Isn't any smart pointer with a reset() method applicable? The memory
> > owner can declare a smart instead of a naked pointer, and call reset()
> > instead of delete. Everything else would remain the same. The
> > advantage would be exception safety and a clear convention for who
> > owns the memory at any given time.
>
> But what does that buy you over a raw pointer?

The usual RAII goodness: sneaky control-flow scenarios can't rob you
of a chance to call delete, compiler-generated destructors and copy
constructors do the right thing, etc. With smart pointers there's
also an explicit and standard convention for who owns the memory,
which I find more readable than the implicit conventions necessary
with raw pointers.

> such entity objects will be
> referenced through some sort of map; they are known outside the
> program

Is the container object meant to own the memory pointed to by its
elements? If so, RAII is definitely a good idea. If not, then what I
said up top doesn't apply to the container but to the real owner. The
container merely needs an assignable reference, and yes, a naked
pointer is a poor-man's assignable reference. But it is not without
baggage or alternatives -- see below.

> To delete the object, the user (or the object itself) removes
> itself from the map.

That's an option, though it isn't what I was suggesting. Assuming
it's a map of shared_ptr's, to delete the object the user calls
reset() on the relevant map element. You can then remove the element
or not, matching exactly what you'd do with a raw pointer.

> Note too that if you use
> the registry.erase() method, the object might not be destructed
> immediately; someone might still be using it somewhere, and hold
> a shared_ptr to it as well.

If someone else does hold a shared_ptr, that's a signal that the
object mustn't be destroyed yet. In your scenario, I'd have that
someone hold a weak_ptr, signaling that they don't mind the memory
getting deleted on them. You see what I meant about explicit
conventions?

Al

unread,
Mar 3, 2007, 6:24:36 AM3/3/07
to
Hi,

James Kanze wrote:
<snip>


>> Alternatively, I've been experimenting with changing some existing raw
>> pointers into either:
>
>> CheckedPointer<T> or
>> OptionalPointer<T>
>
>> Where the former requires (and asserts) that its pointer never be null,
>> and the latter allows a null pointer, but asserts that a null pointer is
>> never dereferenced -- any feedback on this idea is appreciated :).
>
> I don't see much advantage in the second; all of the systems I
> work on fail when a null pointer is dereferenced anyway. The
> former might be interesting in some contexts, although the name
> is a little vague; maybe NonNullPointer<T>?

Yeah, the names need a little work. Regarding the advantage, there are a
couple for me. First, I'm paranoid in that the a null dereference is, I
believe, undefined behavior, so that failure is not guaranteed. In
addition, for debugging purposes, I've found it easier to manage using
these.

For instance, you can set a single permanent breakpoint, and you can
avoid the mess that would have happened otherwise. Finally, the stack in
the debugger is usually cleaner because the error didn't go any further.

One other potential use is to provide a graceful exit / exception for
the program in the event the error occurs, but I haven't done this yet.

I'm forgetting a couple others.

Cheers.
-Al.

Al

unread,
Mar 3, 2007, 6:22:06 AM3/3/07
to
Hi,

Daniel Krügler wrote:
<snip>


>> Where the former requires (and asserts) that its pointer never be null,
>> and the latter allows a null pointer, but asserts that a null pointer is
>> never dereferenced -- any feedback on this idea is appreciated :).
>
> IMO, the name CheckedPointer is not very meaningful, especially
> in contrast to OptionalPointer (which says more explicitely what
> it wants). What about ValuePointer?

Yep, I think this is a good point. How about RequiredPointer<>?

> One might also consider to seperate the concerns of holding and
> preconditions by means of a proper policy. I remember some very
> advanced ansatz (I would say it was from Andrei Alexandrescu),
> that distinguished three poly domains.

I haven't used policies with these because they are supposed to be very
simple utility classes, without any sort of configuration.

>> If you're interested in the code, let me know.
>
> Yes, would be nice to see.

This is what I have so far:

(CheckedPointer is roughly the same thing but with the asserts
uncommented). Comments/Suggestions welcome. Thanks!

---

// Util/OptionalPointer.hpp
#include <cassert>

namespace Util {

template <typename T>
class OptionalPointer {
T* P;

public:

inline OptionalPointer(T *const p = 0) : P(p) { /*assert(p);*/ }
inline OptionalPointer& operator = (T *const p) {
/*assert(p);
assert(P);*/
P = p;
return *this;
}

inline operator bool() const { return P != 0; }
inline T* get() const { assert(P); return P; }
inline T& operator* () const { return *get(); }
inline T* operator->() const { return get(); }
};

} // namespace Util.

---

Cheers,
-Al.

Al

unread,
Mar 3, 2007, 6:21:48 AM3/3/07
to
Hi,

Emmanuel Deloget wrote:
> On 28 fév, 12:47, Al <t...@haik.us> wrote:
>> Hi,
>>
>> Sushrut Sardeshmukh wrote:
>>> Should we stop using naked pointer and replace all of them with
>>> boost:shared_ptr or ( shared_array<T> or scoped_ptr) ?
>>> I am trying to come up with C to C++ conversion guidelines for a large
>>> transformation project. Your inputs will be very valuable.
>> If you're going to use one of the typical ones, why not std::auto_ptr?
>> shared_ptr has semantics which are often inconvenient / incorrect.
>
> Are you sure you didn't inverted something? To me, it seems that the
> semantics of share_ptr (CopyConstructible, Assignable) are more useful
> than the TransferOwnership semantic of auto_ptr, which prohibits its
> use in many cases.
>
> Or did I miss something about shared_ptr?
>
> -- Emmanuel Deloget


Most of the times I've needed pointers it is to interface with C APIs.
In such cases, typically you don't need the pointer to outlive the
function call it is created in.

Thus, auto_ptr serves to RAII-ize the resource better than shared_ptr, I
think. Its main limitation is that it doesn't allow for a custom
deleter, which is unfortunate.

Cheers,
-Al.

James Kanze

unread,
Mar 3, 2007, 9:56:39 AM3/3/07
to
Dejan.M...@gmail.com wrote:
> On Mar 2, 6:31 am, "James Kanze" <james.ka...@gmail.com> wrote:
> > On Mar 2, 4:33 am, "Dejan.Mircev...@gmail.com"

> > <Dejan.Mircev...@gmail.com> wrote:
> > > On Mar 1, 5:10 am, "James Kanze" <james.ka...@gmail.com> wrote:
> > > > In my own applications, I find that most pointers are to objects
> > > > with explicit lifetimes, and I've yet to find a smart pointer
> > > > which is applicable to them (although I've tried).
> > > Isn't any smart pointer with a reset() method applicable? The memory
> > > owner can declare a smart instead of a naked pointer, and call reset()
> > > instead of delete. Everything else would remain the same. The
> > > advantage would be exception safety and a clear convention for who
> > > owns the memory at any given time.

> > But what does that buy you over a raw pointer?

> The usual RAII goodness: sneaky control-flow scenarios can't rob you
> of a chance to call delete, compiler-generated destructors and copy
> constructors do the right thing, etc.

You missed the point. RAII doesn't work in this case, at least
not in its general meaning. The destruction of the object is
triggered by an explicit, external event, and not the fact that
you leave scope.

> With smart pointers there's
> also an explicit and standard convention for who owns the memory,
> which I find more readable than the implicit conventions necessary
> with raw pointers.

In this case, the object itself owns the memory. This is, after
all, the classical OO idiom, and the object has identity and
behavior. It doesn't make sense for anyone else to own the
memory.

Note that in a larger sense, such objects often outlive the
process which creates them; in my applications, they've almost
always been persistent.

> > such entity objects will be
> > referenced through some sort of map; they are known outside the
> > program

> Is the container object meant to own the memory pointed to by its
> elements?

No. Didn't you read what I said? The container is for
navigation, not ownership. The object isn't "owned"; it just
is.

> If so, RAII is definitely a good idea. If not, then what I
> said up top doesn't apply to the container but to the real owner.

The real owner is the object itself. But "this" can't be a
smart pointer, and it makes no sense for it to be one, since the
object exists even when no member functions are being called.

More reasonably, of course, is just to recognize that the object
doesn't participate in any sort of relationship like
"ownership".

> The
> container merely needs an assignable reference, and yes, a naked
> pointer is a poor-man's assignable reference. But it is not without
> baggage or alternatives -- see below.

> > To delete the object, the user (or the object itself) removes
> > itself from the map.

> That's an option, though it isn't what I was suggesting. Assuming
> it's a map of shared_ptr's, to delete the object the user calls
> reset() on the relevant map element.

No. You remove the entry from the map. If the object doesn't
exist anymore, why should you be able to navigate to a dead end.

> You can then remove the element or not, matching exactly what
> you'd do with a raw pointer.

> > Note too that if you use
> > the registry.erase() method, the object might not be destructed
> > immediately; someone might still be using it somewhere, and hold
> > a shared_ptr to it as well.

> If someone else does hold a shared_ptr, that's a signal that the
> object mustn't be destroyed yet.

No. That's a signal that there is an error in your software,
which must be corrected.

> In your scenario, I'd have that
> someone hold a weak_ptr, signaling that they don't mind the memory
> getting deleted on them.

Except that afterwards, whoever is holding the weak_ptr has to
delete it, or you leak memory. Generally, if other objects
which hold pointers (which can navigate directly to the object)
must be notified, using some variant of the observer pattern, so
that they can remove the pointer.

> You see what I meant about explicit
> conventions?

It sounds to me like your explicit convention is forcing a
square peg into a round hole.

--
James Kanze (Gabi Software) email: james...@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

--

Al

unread,
Mar 4, 2007, 2:30:43 AM3/4/07
to
Hi,

> The OptionalPointer seems like a reasonable idea. I would personally
> have a hard time finding a use for CheckedPointer.
>
> In the majority of cases I would think your CheckedPointer should just
> be a reference. In cases where you need it to also be assignable, it
> seems to just duplicate boost::reference_wrapper<T>.

The problem is that I need to store these in containers (vectors and
lists), which I don't think would work using references. Does
boost::reference_wrapper allow you to do this?

Thanks,
-Al.

--

James Kanze

unread,
Mar 4, 2007, 7:24:45 AM3/4/07
to
On Mar 3, 12:24 pm, Al <t...@haik.us> wrote:
> James Kanze wrote:

> <snip>

> >> Alternatively, I've been experimenting with changing some existing raw
> >> pointers into either:

> >> CheckedPointer<T> or
> >> OptionalPointer<T>

> >> Where the former requires (and asserts) that its pointer never be null,
> >> and the latter allows a null pointer, but asserts that a null pointer is
> >> never dereferenced -- any feedback on this idea is appreciated :).

> > I don't see much advantage in the second; all of the systems I
> > work on fail when a null pointer is dereferenced anyway. The
> > former might be interesting in some contexts, although the name
> > is a little vague; maybe NonNullPointer<T>?

> Yeah, the names need a little work. Regarding the advantage, there are a
> couple for me. First, I'm paranoid in that the a null dereference is, I
> believe, undefined behavior, so that failure is not guaranteed.

Yes. It depends on your target platforms. I currently write
software for large scale servers, so I can "portably" count on a
core dump when I dereference a null pointer (just as I
"portably" count on pthread_mutex_t, etc.).

> In
> addition, for debugging purposes, I've found it easier to manage using
> these.

What's the difference between an assertion failure and what
happens if you actually dereference the pointer?

> For instance, you can set a single permanent breakpoint, and you can
> avoid the mess that would have happened otherwise.

Except that the breakpoint is only present when you're running
under the debugger, which is rarely the case, and "the mess that
would have happened otherwise" is a core dump---precisely what I
want to do a post-mortem.

> Finally, the stack in
> the debugger is usually cleaner because the error didn't go any further.

I find the stack cleaner when I get the core dump: I don't have
the extra entries for the functions called from assert. (The
difference is marginal, however.)

> One other potential use is to provide a graceful exit / exception for
> the program in the event the error occurs, but I haven't done this yet.

I'm not sure you want (or can have) a graceful exit; you want
that core dump, to find out exactly what happened.

The issues may be different in shrink wrapped software, however,
where you can't access the core dump anyway.

--
James Kanze (Gabi Software) email: james...@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

--

Ulrich Eckhardt

unread,
Mar 5, 2007, 7:08:26 AM3/5/07
to
James Kanze wrote:
> Dejan.M...@gmail.com wrote:
>> On Mar 2, 6:31 am, "James Kanze" <james.ka...@gmail.com> wrote:
>>> On Mar 2, 4:33 am, "Dejan.Mircev...@gmail.com"
>>> <Dejan.Mircev...@gmail.com> wrote:
>>>> On Mar 1, 5:10 am, "James Kanze" <james.ka...@gmail.com> wrote:
>>>>> In my own applications, I find that most pointers are to objects
>>>>> with explicit lifetimes, and I've yet to find a smart pointer
>>>>> which is applicable to them (although I've tried).
>>>> Isn't any smart pointer with a reset() method applicable? The memory
>>>> owner can declare a smart instead of a naked pointer, and call
>>>> reset()
>>>> instead of delete. Everything else would remain the same. The
>>>> advantage would be exception safety and a clear convention for who
>>>> owns the memory at any given time.
>>>
>>> But what does that buy you over a raw pointer?
>>
>> The usual RAII goodness: sneaky control-flow scenarios can't rob you
>> of a chance to call delete, compiler-generated destructors and copy
>> constructors do the right thing, etc.
>
> You missed the point. RAII doesn't work in this case, at least
> not in its general meaning. The destruction of the object is
> triggered by an explicit, external event, and not the fact that
> you leave scope.

I'd agree that there is some external event happening that has some
implications. So much is dictated by the circumstances. What is not
dictated is how that is modeled in C++, that is rather a question of
choice.

>> With smart pointers there's also an explicit and standard convention
>> for who owns the memory, which I find more readable than the
>> implicit conventions necessary with raw pointers.
>
> In this case, the object itself owns the memory. This is, after
> all, the classical OO idiom, and the object has identity and
> behavior. It doesn't make sense for anyone else to own the
> memory.

How about replacing the verb "own" with "reference"? It may be that the
object itself owns its memory, but other parts of the program might still
reference the memory via the object. Now, there are several ways to model
that, but I'll assume that one thing must not happen: dangling
references/pointers. The reason is that C++ doesn't provide any means to
detect such an invalid reference (i.e. it's UB) and they typically present
hard to detect errors when they happen.

I see two ways to achieve this goal:
1. The object doesn't get destroyed until the last reference to it is gone.
This is what is easiest modeled with shared ownership using reference
counting or garbage collection. Note that this is only about the C++
object. The application logic might mandate via external that the object is
destroyed, but as far as C++ is concerned the object still exists,
typically as a defunct shell (e.g. when unplugging a thumbdrive).
2. Before destroying, all references to the object are reset to a detectable
state (e.g. null pointer). This requires knowledge of every reference to
the object.

Now, considering smart pointers, those just help modelling the above. For
the first case, you simply use a refcounting pointer like boost::shared_ptr
or boost::intrusive_ptr. The external event then simply transitions the
object to a defunct state.

For the second case, you can use a single boost::shared_ptr inside the
object itself (yes, this doesn't give you scoped access or RAII) and
externally only store boost::weak_ptrs. If the external triggers the
disappearance of the object, it will only reset its shared_ptr to itself
which will first invalidate all the weak_ptrs still referencing the object
and then finally delete the C++ object.

All this even works pretty well in a multithreaded program, but then there
is one thing to consider: several threads might make a call to the object's
members at once, so probably it will have some kind of mutex. Now, this is
a real case of shared ownership, because you can't destroy the mutex while
some other object is waiting for it. Therefore, it is often desirable to
use the approach that leaves a defunct shell of the objects.


>>> Note too that if you use
>>> the registry.erase() method, the object might not be destructed
>>> immediately; someone might still be using it somewhere, and hold
>>> a shared_ptr to it as well.
>
>> If someone else does hold a shared_ptr, that's a signal that the
>> object mustn't be destroyed yet.
>
> No. That's a signal that there is an error in your software,
> which must be corrected.
>
>> In your scenario, I'd have that
>> someone hold a weak_ptr, signaling that they don't mind the memory
>> getting deleted on them.
>
> Except that afterwards, whoever is holding the weak_ptr has to
> delete it, or you leak memory. Generally, if other objects
> which hold pointers (which can navigate directly to the object)
> must be notified, using some variant of the observer pattern, so
> that they can remove the pointer.

Sorry, but I think you don't understand weak_ptr. In fact it is an
implementation of this observer pattern, i.e. it automatically becomes a
null pointer once the last shared_ptr to the object is reset. Yes, if you
need additional information than "has vanished" it isn't enough, also if
you need to handle the information right now instead of checking the
pointer on next occasion, but it still is good enough for many cases. Also,
and that is one big advantage IMHO, it has semantics defined by the types,
i.e. there is some meaning associated with shared_ptr and weak_ptr which
isn't the case with a raw pointer.

Uli

--
Sator Laser GmbH
Geschäftsführer: Ronald Boers Steuernummer: 02/858/00757
Amtsgericht Hamburg HR B62 932 USt-Id.Nr.: DE183047360

Dejan.M...@gmail.com

unread,
Mar 5, 2007, 1:32:25 PM3/5/07
to
(Moderator: sorry if this is a duplicate submission; the previous one
seems to have been lost.)

> You missed the point. RAII doesn't work in this case, at least
> not in its general meaning. The destruction of the object is
> triggered by an explicit, external event, and not the fact that
> you leave scope.

Sorry if I'm slow to grasp your object model. I'm piecing it together
from several posts, probably making some incorrect assumptions along
the way. If you think it's worth continuing this discussion, can you
take a moment to verify my assumptions about your present solution,
the one with raw pointers? This is what I've gleaned so far:

- you have objects that control their own lifespan and will at some
point self-destruct

- clients reference such objects by looking up a key in a registry

- successful lookup yields a raw pointer to the sought object; I'm not
sure why a reference wouldn't suffice -- perhaps you believe that
would complicate the registry implementation too much

- I'm not sure what a client may do with the raw pointer; obviously,
it mustn't delete it, but what about copying it?

- I'm also not sure how the client ensures that the pointer it's about
to use remains valid (what if the object has self-destroyed already,
perhaps in another thread?)

- when an object self-destructs, the registry is automagically updated
so future lookups of the object's key will fail; I'm not sure how
exactly this is done

- the memory occupied by these objects is obtained from some allocator
that requires it to be reclaimed using delete; I'm not sure why
dynamic allocation is absolutely necessary, but I presume there are
good reasons

- the objects are also persisted, though I'm not sure how to square
this against the previous fact; perhaps in-memory objects are a copy
of the persisted ones? or are they mmap-ed, and the allocator operates
on disk space?

- the objects themselves ensure that delete is called when appropriate
by invoking "delete this" in some of their methods; hopefully they do
this in the destructor, obtaining all the benefits of RAII; hopefully
they also steer clear of undefined behavior, which is very easy to
introduce if you "delete this"; I'm not sure if and how the code
prevents creation of these objects in automatic or static memory (eg,
by template code)

> Except that afterwards, whoever is holding the weak_ptr has to
> delete it, or you leak memory.

Hmmmm, that's not how I understand weak_ptr to work. Are we talking
about the same thing?

http://www.boost.org/libs/smart_ptr/smart_ptr.htm

Dejan.M...@gmail.com

unread,
Mar 5, 2007, 4:57:44 PM3/5/07
to
On Mar 5, 1:32 pm, "Dejan.Mircev...@gmail.com"

<Dejan.Mircev...@gmail.com> wrote:
>
> - the objects themselves ensure that delete is called when appropriate
> by invoking "delete this" in some of their methods; hopefully they do
> this in the destructor, obtaining all the benefits of RAII;

Actually, I take this back: no matter where you put "delete this," you
can't guarantee memory reclamation in the event of an exception that
cuts off references to the object.

James Kanze

unread,
Mar 6, 2007, 5:15:20 AM3/6/07
to
Dejan.M...@gmail.com wrote:
> > You missed the point. RAII doesn't work in this case, at least
> > not in its general meaning. The destruction of the object is
> > triggered by an explicit, external event, and not the fact that
> > you leave scope.

> Sorry if I'm slow to grasp your object model. I'm piecing it together
> from several posts, probably making some incorrect assumptions along
> the way. If you think it's worth continuing this discussion, can you
> take a moment to verify my assumptions about your present solution,
> the one with raw pointers? This is what I've gleaned so far:

> - you have objects that control their own lifespan and will at some
> point self-destruct

More or less. I have objects which have an explicit lifetime.
Their lifetime doesn't depend on random internal factors like
who might or might not hold a pointer to them.

> - clients reference such objects by looking up a key in a registry

Originally, at least. Most of the time. That may be an
artifact of my applications (servers, where the "clients"
actually run on a different machine), however; I can easily
imagine cases where all of the clients actually do hold
pointers (e.g. GUI applications).

> - successful lookup yields a raw pointer to the sought object; I'm not
> sure why a reference wouldn't suffice -- perhaps you believe that
> would complicate the registry implementation too much

Well, lookup isn't necessarily successful, so you need a null
pointer. And of course, sometimes, the client will have to
reseat it, and very, very often, the raw pointers will end up in
containers.

> - I'm not sure what a client may do with the raw pointer; obviously,
> it mustn't delete it, but what about copying it?

It almost certainly will copy it. It will also call member
functions through it---that is generally the motivation for
having it. In many cases, in fact, it will start by calling a
member function to register itself as an observer of the object,
so that it can be notified of changes of state of the object.

> - I'm also not sure how the client ensures that the pointer it's about
> to use remains valid (what if the object has self-destroyed already,
> perhaps in another thread?)

Design. The relationships between objects are a vital part of
design.

> - when an object self-destructs, the registry is automagically updated
> so future lookups of the object's key will fail; I'm not sure how
> exactly this is done

When an object self-destructs, it's destructor is called. Which
does whatever is necessary to ensure that the object isn't used
in the future---it removes it from any registry, informs
observers, etc.

> - the memory occupied by these objects is obtained from some allocator
> that requires it to be reclaimed using delete; I'm not sure why
> dynamic allocation is absolutely necessary, but I presume there are
> good reasons

Because the object has arbitrary lifetime, and dynamic memory is
the only means to obtain arbitrary lifetime in C++.

> - the objects are also persisted, though I'm not sure how to square
> this against the previous fact; perhaps in-memory objects are a copy
> of the persisted ones? or are they mmap-ed, and the allocator operates
> on disk space?

Persistence depends on the application.

In my present application, for example, the server is stopped at
the end of the day, and restarted every morning. (This is,
IMHO, rather exceptional.) Objects will almost never be deleted
the same day they are created, however. On start-up, objects
are created from the persistent data base; all updates are also
mirrored to the persistent data base (before being acknowledged
to the client). And objects are also deleted---in our case,
because they have been "closed" for at least 24 hours. (When
they are deleted, they are in fact moved to a second persistent
database, backed up once a week on mag tape, because legally,
the data the objects represent is required to be kept
forever---or at least something like fifty or a hundred years.)

All of my previous server applications have run 24 hours a day,
7 days a week. Persistence was only used "just in case", if the
machine crashed, or something like that. Objects were created
as a result of client requests, and deleted as a result of other
client requests.

In the one large GUI application I worked on, except for
persistence, one could say it worked in a similar fashion.
Objects were created as a result of client interaction (in this
case, the client was the user, and his "requests" took the form
of mouse clicks and keyboard input), and were later deleted as a
result of other client interaction. But the same rules applied:
if a panel was removed from screen, because of some user action,
the object which represented it automatically became invalid,
and was "deleted". (In fact, the application was in Java, so
there was no delete, but rather a special member function
dispose(), which did whatever had to be done. I might add that
we modified Swing to support this, because we had problems
managing the lifetime of objects.)

In no case does the lifetime of an object depend on who has a
pointer to it. The problem is rather the reverse: when the
object's lifetime ends, you have to ensure that all interested
parties are notified.

> - the objects themselves ensure that delete is called when appropriate
> by invoking "delete this" in some of their methods; hopefully they do
> this in the destructor,

I would definitly not recommend using "delete this" in the
destructor.

I think you're getting hung up on the "delete this". I've found
that it is usually convenient (and very OO:-)) to let the object
manage its own lifetime, but the important point is not who does
the delete, but the fact that it is an explicit action, taken in
response to an explicit stimulus, and is not "garbage
collection", or anything along those lines. (And that it is
really independant of memory management---even with garbage
collection, such objects need explicit lifetime management,
because their lifetime is part of the design.)

> obtaining all the benefits of RAII;

There are no benefits of RAII in this case. In fact, there is
absolutely no way to use it, except to artificially create other
objects, which have a manually managed lifetime. Technically,
what RAII does is associate the lifetime of the managed resource
to the lifetime of another object. It's a very powerful idiom
when you have another object which already has the correct
lifetime, and even more powerful when the lifetime corresponds
to one of the lifetimes C++ manages automatically. It's totally
irrelevant when the "resource" is itself an object, and the
lifetime of that object must be managed explicitly anyway.

> hopefully
> they also steer clear of undefined behavior, which is very easy to
> introduce if you "delete this"; I'm not sure if and how the code
> prevents creation of these objects in automatic or static memory (eg,
> by template code)

Common sense, mostly. The semantics of the object are such that
it wouldn't even occur to anyone to create one other than
dynamically. The objects don't support copy or assignment, and
the semantics require explicit lifetime, with an explicit
creation (thus, no static lifetimes) and an explicit destruction
as a reaction to a different stimilus, at some unknown point in
the future.

> > Except that afterwards, whoever is holding the weak_ptr has to
> > delete it, or you leak memory.

> Hmmmm, that's not how I understand weak_ptr to work. Are we talking
> about the same thing?

> http://www.boost.org/libs/smart_ptr/smart_ptr.htm

I'm not sure I was clear: "whoever is holding the weak_ptr has
to delete the weak_ptr". Surely you have 1 to n relationships
in your software. How do you manage those? I generally use an
std::set< T* >, and register as an observer with whoever is
responsible for the lifetime of the T---the T objects
themselves, if "delete this" is being used, or with whoever is
responsible for the object lifetime otherwise.

--
James Kanze (GABI Software) email:james...@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

--

James Kanze

unread,
Mar 6, 2007, 5:13:04 AM3/6/07
to

And that RAII doesn't really apply. RAII is used to associate
the life of a dynamically allocated resource to the life of some
other object (typically, and most usefully, an object with
automatic lifetime). In this case, there is no other object
which has the right lifetime.

> >> With smart pointers there's also an explicit and standard convention
> >> for who owns the memory, which I find more readable than the
> >> implicit conventions necessary with raw pointers.

> > In this case, the object itself owns the memory. This is, after
> > all, the classical OO idiom, and the object has identity and
> > behavior. It doesn't make sense for anyone else to own the
> > memory.

> How about replacing the verb "own" with "reference"? It may be that the
> object itself owns its memory,

It's more than just memory; it's the object, as an object. (If
memory is the only problem, the Boehm collector handles that
just nicely.)

> but other parts of the program might still
> reference the memory via the object. Now, there are several ways to model
> that, but I'll assume that one thing must not happen: dangling
> references/pointers.

I presume that when you say "reference the memory via the
object", you mean "reference the memory as if it were a valid
instance of the object". And I agree, dangling pointers should
be avoided.

> The reason is that C++ doesn't provide any means to detect
> such an invalid reference (i.e. it's UB) and they typically
> present hard to detect errors when they happen.

> I see two ways to achieve this goal:
> 1. The object doesn't get destroyed until the last reference to it is gone.
> This is what is easiest modeled with shared ownership using reference
> counting or garbage collection. Note that this is only about the C++
> object. The application logic might mandate via external that the object is
> destroyed, but as far as C++ is concerned the object still exists,
> typically as a defunct shell (e.g. when unplugging a thumbdrive).

Doesn't help. The object ceases to exist as a valid object
because of an external event, whether you hold pointers to it or
not, and whether the destructor has been called or not. One of
the reactions to that external event must be to notify all other
objects which hold pointers to the object, so that they know to
not use the pointer. (Typically, they will null it, if it is a
single pointer, and remove it completely from the container if
it is in a container.) If you're not doing this, nothing works,
regardless of the type of pointer you use. And if you're doing
it, there's absolutely no point in using any sort of fancy
pointer; raw pointers work just as well.

> 2. Before destroying, all references to the object are reset to a detectable
> state (e.g. null pointer). This requires knowledge of every reference to
> the object.

That is, in fact, the only solution which works. Except that if
the pointer is in a container, for example, you don't want to
just set it to null; you want to remove it from the container.
And of course, whoever was counting on the object might have
other things to do as well. (Thus, for example, in a telecoms
application, loosing a PCB means notifying all of the
connections which use that PCB; the connections don't just null
the pointer to the implementing PCB, they activate the back-up
connection is one exists, change state, etc.)

> Now, considering smart pointers, those just help modelling the above.

Many places where I've worked have tried to find a generic
solution to the second, above. It's usually called relationship
management, not smart pointers, of course, and to date, I've yet
to see anything general that works.

> For
> the first case, you simply use a refcounting pointer like boost::shared_ptr
> or boost::intrusive_ptr. The external event then simply transitions the
> object to a defunct state.

In which case, you might as well use the Boehm collector, and be
done with it. It works even better.

> For the second case, you can use a single boost::shared_ptr inside the
> object itself (yes, this doesn't give you scoped access or RAII) and
> externally only store boost::weak_ptrs. If the external triggers the
> disappearance of the object, it will only reset its shared_ptr to itself
> which will first invalidate all the weak_ptrs still referencing the object
> and then finally delete the C++ object.

But it won't remove the weak_ptr's from containers, nor cause
the objects using them to change state, activate back-ups or
what have you. It only does a very small part of the job, and
anything which does the rest also handles this part.

> All this even works pretty well in a multithreaded program, but then there
> is one thing to consider: several threads might make a call to the object's
> members at once, so probably it will have some kind of mutex. Now, this is
> a real case of shared ownership, because you can't destroy the mutex while
> some other object is waiting for it. Therefore, it is often desirable to
> use the approach that leaves a defunct shell of the objects.

Generally, in such a system, mutex's have to be held at the
transaction level. Precisely for this reason. (Independantly
of whether an object is waiting on the mutex, you can't allow
another object to obtain a pointer to your object without
creating the necessary observer, so that it can be notified when
you die. Otherwise, you inevitably do get dangling pointers.)

[...]


> >> In your scenario, I'd have that
> >> someone hold a weak_ptr, signaling that they don't mind the memory
> >> getting deleted on them.

> > Except that afterwards, whoever is holding the weak_ptr has to
> > delete it, or you leak memory. Generally, if other objects
> > which hold pointers (which can navigate directly to the object)
> > must be notified, using some variant of the observer pattern, so
> > that they can remove the pointer.

> Sorry, but I think you don't understand weak_ptr. In fact it is an
> implementation of this observer pattern, i.e. it automatically becomes a
> null pointer once the last shared_ptr to the object is reset.

Exactly. It doesn't remove itself from the container which
contained it. You leak.

For that matter: most of the time, my collection is and
std::set< T* >. Can you even use weak_ptr's in a set? What
happens to the ordering if one nulls itself?

Shared_ptr do have uses (especially if for some reason you
cannot use the Boehm collector), but those uses only affect a
small percentage of the total objects. As I have already said,
most of the time, value objects shouldn't be dynamically
allocated to begin with, and true entity objects have an
explicit lifetime dependant on their semantics, and thus require
explicit management. And such objects generally represent well
over 90% of the objects in an application.

I tried systematically using boost::shared_ptr in an
application, and backed out. In the end (in this particular
application), the only place where it was really relevant had a
cycle, which had to be managed explicitly to ensure the correct
order of deletion. In other applications, there will be some
uses for it, but...

The orginal question was: "Should we stop using naked pointer


and replace all of them with boost:shared_ptr or

(shared_array<T> or scoped_ptr)?" And the answer to that is
simply NO. The poster is looking for a silver bullet, and there
isn't one. You have to think about object lifetime, regardless.
It's a design issue, which must be addressed at the design
level. And once you've addressed it at that level, you'll find
that different types of pointers are appropriate in different
cases, and that raw pointers are appropriate in a surprising
number of cases, probably more that shared_ptr or scoped_ptr.

> Yes, if you
> need additional information than "has vanished" it isn't enough, also if
> you need to handle the information right now instead of checking the
> pointer on next occasion, but it still is good enough for many cases. Also,
> and that is one big advantage IMHO, it has semantics defined by the types,
> i.e. there is some meaning associated with shared_ptr and weak_ptr which
> isn't the case with a raw pointer.

Which is, in some ways, a problem, because the semantics (what
you are telling the reader) aren't really the whole truth.
There are cases where they are, and in such cases, it is quite
appropriate to use such pointers---although I can't quite come
up with a scenario where boost::weak_ptr would be appropriate.
But any time the semantics are that the object has its own,
explicit lifetime (logically, at least), then using shared_ptr
or something similar is lying to the reader; it tells him
something about the object that isn't true.

--
James Kanze (GABI Software) email:james...@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

--

Peter Dimov

unread,
Mar 6, 2007, 11:48:57 AM3/6/07
to
On Mar 6, 12:13 pm, "James Kanze" <james.ka...@gmail.com> wrote:

> Can you even use weak_ptr's in a set?

Yes, you can.

> What happens to the ordering if one nulls itself?

Stays the same.

Dejan.M...@gmail.com

unread,
Mar 7, 2007, 2:58:28 AM3/7/07
to
On Mar 6, 11:48 am, "Peter Dimov" <pdi...@gmail.com> wrote:
> On Mar 6, 12:13 pm, "James Kanze" <james.ka...@gmail.com> wrote:
>
> > Can you even use weak_ptr's in a set?
>
> Yes, you can.
>
> > What happens to the ordering if one nulls itself?
>
> Stays the same.

I think you two are assuming different comparators.

James Kanze

unread,
Mar 7, 2007, 10:36:49 AM3/7/07
to
On Mar 7, 8:58 am, "Dejan.Mircev...@gmail.com"

<Dejan.Mircev...@gmail.com> wrote:
> On Mar 6, 11:48 am, "Peter Dimov" <pdi...@gmail.com> wrote:

> > On Mar 6, 12:13 pm, "James Kanze" <james.ka...@gmail.com> wrote:

> > > Can you even use weak_ptr's in a set?

> > Yes, you can.

> > > What happens to the ordering if one nulls itself?

> > Stays the same.

> I think you two are assuming different comparators.

I don't think so. I'm talking about the default comparator
(std::less), and I think Peter is as well. I just naïvely
assumed that it would compare the actual pointers to the object;
Peter (indirectly) has assured me that it doesn't (since
changing the pointer to null doesn't disturb the ordering).

I'll admit that I don't know too much about boost::weak_ptr,
because I've never found a use for it. As I said, I generally
want not only to invalidate the pointer, but to remove it
completely, and often, to do other things as well.

--
James Kanze (GABI Software) email:james...@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

--

Dejan.M...@gmail.com

unread,
Mar 7, 2007, 5:39:18 PM3/7/07
to
On Mar 6, 5:13 am, "James Kanze" <james.ka...@gmail.com> wrote:
>
> The orginal question was: "Should we stop using naked pointer
> and replace all of them with boost:shared_ptr or
> (shared_array<T> or scoped_ptr)?" And the answer to that is
> simply NO. The poster is looking for a silver bullet, and there
> isn't one. You have to think about object lifetime, regardless.

Oh, I absolutely agree with this. I'm hard-pressed to think of anyone
in this thread who disputed it.

What I am disputing is your claims about raw pointers. They range
from incredible (weak_ptr leaks) through puzzling (map lookup needs
raw pointers to signal failure) to intriguing but refutable (raw
pointers are more useful than smart pointers).

Your example with self-destructing objects is excellent for
accentuating the similarities and differences between raw and smart
pointers. All the problems around copying pointers and maintaining
their validity are exactly the same for both raw and smart pointers.
For example, the weak_ptr-in-a-set problem you indicate is still
present with a raw pointer, and for the same reason. If these
problems are already solved by your design, they can be solved
analogously with smart pointers, with very little code change.

But the genuine difference here is that your usage of raw pointers
forces you to be extremely cautious in your implementation: you flirt
with undefined behavior [FAQ Lite 16.15], and if an exception is ever
thrown that destroys your registry, you will leak all your objects. A
smart-pointer solution would free you from these worries. For this
reason, and because of the explicit ownership conventions, a smart-
pointer solution would be more maintainable.

The more interesting question you raise is how do we allow object
lifetimes that don't coincide with scopes. Dynamic allocation using
operator new is one option (don't forget to use smart pointers,
though ;). But another option is putting objects into STL containers
using value semantics. If there's no danger of slicing, I find this
option very appealing. (Whether a container might use new/delete
under the hood is irrelevant, since I don't have to worry about
leaking that memory.) In fact, you could very likely solve your
example by putting the objects themselves into your map and passing
around references to them. The same map-updating mechanism you use
today would let you remove them and invalidate outstanding references
when they die.

Dejan.M...@gmail.com

unread,
Mar 7, 2007, 5:39:10 PM3/7/07
to
On Mar 6, 5:15 am, "James Kanze" <james.ka...@gmail.com> wrote:
>
> Surely you have 1 to n relationships
> in your software. How do you manage those? I generally use an
> std::set< T* >

OK, this is where I'll agree with you: raw pointers are a suitable
solution when you need an assignable reference to externally owned
memory that may be non-dynamic. I know only two examples of this: one
is containers of such references (as in your example above), and the
other is "switch" variables, which point to one thing or another
depending on control flow. Everywhere else, I'd use references or
smart pointers. And if we ever get assignable references in the
language, this use of raw pointers may cease to be justifiable.

Also, I believe this is orthogonal to the question of arbitrary
lifespans.

Ulrich Eckhardt

unread,
Mar 7, 2007, 6:12:54 PM3/7/07
to
Dejan.M...@gmail.com wrote:
> On Mar 6, 11:48 am, "Peter Dimov" <pdi...@gmail.com> wrote:
>> On Mar 6, 12:13 pm, "James Kanze" <james.ka...@gmail.com> wrote:
>>
>> > Can you even use weak_ptr's in a set?
>>
>> Yes, you can.
>>
>> > What happens to the ordering if one nulls itself?
>>
>> Stays the same.
>
> I think you two are assuming different comparators.
>

No, the default std::less, which uses operator< should work. The point is
that indeed a weak_ptr is nulled when its object's reference count reaches
zero and is deleted. However, exactly to avoid this problem when used as
key in associative containers, such a pointer provides a strict-weak
ordering that does not depend on the underlying shared_ptr.

Note: I remember the exact question from some discussion. It definitely is
not accurately documented, neither in the weak_ptr.hpp header (Boost
1.33.1) nor in the general documentation, AFAICT, apart from the fact that
it mentions associative containers and seems to imply suitability...

<sigh> another point on the evergrowing list of things I'd like to clean up
or see cleaned up.

Uli

--
Sator Laser GmbH
Geschäftsführer: Ronald Boers Steuernummer: 02/858/00757
Amtsgericht Hamburg HR B62 932 USt-Id.Nr.: DE183047360

James Kanze

unread,
Mar 8, 2007, 6:54:24 AM3/8/07
to
On Mar 7, 11:39 pm, "Dejan.Mircev...@gmail.com"

<Dejan.Mircev...@gmail.com> wrote:
> On Mar 6, 5:15 am, "James Kanze" <james.ka...@gmail.com> wrote:

> > Surely you have 1 to n relationships
> > in your software. How do you manage those? I generally use an
> > std::set< T* >

> OK, this is where I'll agree with you: raw pointers are a suitable
> solution when you need an assignable reference to externally owned
> memory that may be non-dynamic.

In my case, it must be dynamic, since the lifetime of the
objects pointed to depends on external events. The non-dynamic
is irrelevant.

[...]


> Also, I believe this is orthogonal to the question of arbitrary
> lifespans.

If the lifespan isn't arbitrary, why allocate the object
dynamically?

--
James Kanze (GABI Software) email:james...@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

--

James Kanze

unread,
Mar 8, 2007, 6:54:13 AM3/8/07
to
> On Mar 6, 5:13 am, "James Kanze" <james.ka...@gmail.com> wrote:

> > The orginal question was: "Should we stop using naked pointer
> > and replace all of them with boost:shared_ptr or
> > (shared_array<T> or scoped_ptr)?" And the answer to that is
> > simply NO. The poster is looking for a silver bullet, and there
> > isn't one. You have to think about object lifetime, regardless.

> Oh, I absolutely agree with this. I'm hard-pressed to think of anyone
> in this thread who disputed it.

That we have to think about object lifetimes? Or that we should
avoid raw pointers? My impression is that a lot of people are
saying that raw pointers are bad.

> What I am disputing is your claims about raw pointers. They range
> from incredible (weak_ptr leaks)

If you count on it, it leaks. The problem isn't with weak_ptr
per se; the problem is that it solves a problem that in practice
is very, very rare. And when used to solve other problems (e.g.
relationship management), the weak_ptr is leaked.

> through puzzling (map lookup needs raw pointers to signal
> failure)

Where did I say that?

> to intriguing but refutable (raw pointers are more
> useful than smart pointers).

Both have their uses. Typically, I'd guess that about 80% of my
pointers are raw pointers. The ration will vary, according to
the application, but I find it hard to imagine a case where
there wouldn't be a significant percentage of raw pointers.

> Your example with self-destructing objects is excellent for
> accentuating the similarities and differences between raw and smart
> pointers. All the problems around copying pointers and maintaining
> their validity are exactly the same for both raw and smart pointers.

Right. You can use make the smart pointers work. It's a little
bit more work, but not much. On the other hand, using smart
pointers in such cases is pretty much lying to the reader, since
there is in fact no "shared ownership". So compared to using
raw pointers, they represent a little extra work, and a lot of
obfuscation. Not what I would consider a net gain.

> For example, the weak_ptr-in-a-set problem you indicate is still
> present with a raw pointer, and for the same reason. If these
> problems are already solved by your design, they can be solved
> analogously with smart pointers, with very little code change.

But why? They do require a slight bit of extra work, an do
nothing but confuse the reader as to what is going on, since
there is no shared ownership, and the pointers don't manage
lifetime.

> But the genuine difference here is that your usage of raw pointers
> forces you to be extremely cautious in your implementation:

I does mean that I have to do some design up front, yes. But
smart pointers don't eliminate the need for design either.

> you flirt
> with undefined behavior [FAQ Lite 16.15], and if an exception is ever
> thrown that destroys your registry, you will leak all your objects.

Now you're being silly. If the registry is ever destroyed, the
whole application crashes, regardless. At that point, what's
one leak more or less?

> A
> smart-pointer solution would free you from these worries.

That's precisely the attitude I'm arguing against. Smart
pointers aren't a silver bullet. A smart-pointer solution won't
free you from these worries. If an exception (or anything else)
causes the registry to be destructed, and I'm still using it, my
code will crash. Period. The correct functionning of my
program depends on the existance of the registry---it is
fundamental to the application. (In fact, the registry is
generally a singleton, or a member of a singleton, and created
in such a way that it will never be destructed. Although in one
particular case, it was in fact a local variable in main.)

> For this
> reason, and because of the explicit ownership conventions, a smart-
> pointer solution would be more maintainable.

Been there, done that. It doesn't work.

> The more interesting question you raise is how do we allow object
> lifetimes that don't coincide with scopes. Dynamic allocation using

> operator new is one option.

It's the only option, according to the C++ standard.

> (don't forget to use smart pointers, though ;).

Which only solve a very small subset of the problems, some of
the time.

> But another option is putting objects into STL containers
> using value semantics.

Entity objects don't have value semantics. They have identity.
They are polymorphic. You can't put them (directly) into an STL
container.

> If there's no danger of slicing, I find this option very
> appealing.

Try it, sometime, with objects which have identity, and don't
support copy or assignment. (As I said earlier, that's about
30% of my objects. And about 75% of those dynamically
allocated.)

> (Whether a container might use new/delete under the hood is
> irrelevant, since I don't have to worry about leaking that
> memory.) In fact, you could very likely solve your example by
> putting the objects themselves into your map and passing
> around references to them.

Except that they are polymorphic, and don't support copy or
assignment.

> The same map-updating mechanism you use today would let you
> remove them and invalidate outstanding references when they
> die.

You don't seem to understand: invalidating outstanding
references is only a small part of the problem. And when you've
solved the rest, the outstanding references won't be there to
invalidate (or will already have been invalidated).

--
James Kanze (GABI Software) email:james...@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

--

Dejan.M...@gmail.com

unread,
Mar 8, 2007, 10:04:58 AM3/8/07
to
On Mar 8, 6:54 am, "James Kanze" <james.ka...@gmail.com> wrote:
>
> using smart
> pointers in such cases is pretty much lying to the reader, since
> there is in fact no "shared ownership".

Are you equating "smart pointer" with shared_ptr? I am not. To me,
"smart pointers" include all of boost/smart_ptr.hpp and <memory>. By
choosing properly among them, I never lie to the reader. In
particular, weak_ptr doesn't imply shared ownership. It conveys
exactly the intent you use the raw pointers for, but it doesn't allow
you to accidentally free the memory or dereference NULL. This is why
your claim that raw pointer is better baffles me.

So there is no silver bullet, and one should always carefully consider
multiple choices. Most of the time, I find a smart pointer to be a
better choice than a raw one, because it helps avoids leaks (FAQ Lite
17.4 provides a flavor of what I'm talking about), lets me write less
code, and explicitly conveys my intent when it comes to sharing and
ownership. As I said before, the only use for raw pointers I have is
as an assignable reference to externally owned memory that may be non-
dynamic (for guaranteed-dynamic memory, I use weak_ptr).

> If an exception (or anything else)
> causes the registry to be destructed, and I'm still using it, my
> code will crash.

Some people prefer to catch the exception and try to recover,
particularly in server code. That was the situation I was
considering. You can't continue using the registry then, since you
can't reference it.

> In fact, the registry is
> generally a singleton, or a member of a singleton, and created
> in such a way that it will never be destructed.

Then your identity objects can never outlive it and can comfortably be
owned by it through shared_ptr. Just don't copy shared_ptr's when you
don't intend to share.

James Kanze

unread,
Mar 9, 2007, 8:45:41 AM3/9/07
to
On Mar 8, 4:04 pm, "Dejan.Mircev...@gmail.com"

<Dejan.Mircev...@gmail.com> wrote:
> On Mar 8, 6:54 am, "James Kanze" <james.ka...@gmail.com> wrote:

> > using smart
> > pointers in such cases is pretty much lying to the reader, since
> > there is in fact no "shared ownership".

> Are you equating "smart pointer" with shared_ptr?

Not necessarily, but it is the only one of the usual lot which
would seem appropriate here.

> I am not. To me,
> "smart pointers" include all of boost/smart_ptr.hpp and <memory>. By
> choosing properly among them, I never lie to the reader.

So which one says that the object has an explicit lifetime, that
it manages itself, and that users of the object must register
with it, so that they can be correctly notified of changes
and/or deletion?

> 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.

> It conveys exactly the intent you use the raw pointers for,
> but it doesn't allow you to accidentally free the memory or
> dereference NULL. This is why your claim that raw pointer is
> better baffles me.

Because it doesn't lie. A weak_ptr (at least a boost::weak_ptr)
supposes a shared_ptr somewhere.

> > If an exception (or anything else)
> > causes the registry to be destructed, and I'm still using it, my
> > code will crash.

> Some people prefer to catch the exception and try to recover,
> particularly in server code.

You can't recover in such a case. By definition. You've lost
it.

> That was the situation I was considering.

Why consider impossibilities? Why not engineer the thing
correctly to begin with?

> You can't continue using the registry then, since you
> can't reference it.

> > In fact, the registry is
> > generally a singleton, or a member of a singleton, and created
> > in such a way that it will never be destructed.

> Then your identity objects can never outlive it and can comfortably be
> owned by it through shared_ptr. Just don't copy shared_ptr's when you
> don't intend to share.

Putting shared_ptr in the registry is a lie, since the registry
doesn't own the object. There's no sharing. There's no
ownership. Anything which forces something which looks like
ownership is lying to the reader.

--
James Kanze (GABI Software) email:james...@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

--

Peter Dimov

unread,
Mar 9, 2007, 1:29:57 PM3/9/07
to
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.
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. 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.

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.

James Kanze

unread,
Mar 9, 2007, 8:58:53 PM3/9/07
to
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...@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

--

Peter Dimov

unread,
Mar 10, 2007, 11:55:18 AM3/10/07
to
On Mar 10, 3:58 am, "James Kanze" <james.ka...@gmail.com> wrote:

> 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.

This is not a problem if your destructor doesn't require a specific
thread and does not depend on a particular lock to be held. These
things are imposed by the need to broadcast the "I'm being destroyed"
message, which calls out to foreign code. If you only destroy the
object in the destructor, there is no need for a lock.

> > 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.

It works for me.

Obviously, if you can guarantee that no other entity is accessing your
object at the moment, you can destroy it _now_ instead of several
microseconds later. I find the bookkeeping that is required to
guarantee that a waste of my time - if the property doesn't already
hold.

> > 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.

On a fairly high level, maybe. It's more to do with "the more
incorrect the granularity, the higher the risk". A low enough level of
lock granularity can eliminate the risk of deadlock. If you never call
a blocking operation while holding a lock, deadlock is impossible.

> And a thread safe shared_ptr has a very low granularity.

Usually none. But that has nothing to do with the topic.

Sebastian Redl

unread,
Mar 10, 2007, 11:56:54 AM3/10/07
to

On Fri, 9 Mar 2007, James Kanze wrote:

> -- 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.

Except that it should be fully accepted if you're used to smart pointers.
Given that one smart pointer is part of the language standard, I just
don't think giving raw pointers such a "priority of expected behaviour" is
fair.

Besides, the standard way to set a smart pointer to null and destruct the
pointee is
ptr.reset()

> I understant the rationale. I just don't think it holds up in
> actual practice.

I strongly disagree. There are quite a few use cases where weak pointers
are useful.

> 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.

For the last 10 posts, this discussion has been about your particular use
case that does not allow for shared_ptr semantics. Seems pointless to me.
Certainly the fact that it doesn't work in your particular case doesn't
invalidate the merits of shared_ptr in principle, much less those of the
concept of smart pointers.

> 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.

Not true. Thread-safety for shared_ptr merely means that the construction
and destruction of the pointer itself is thread-safe. This means:

- The reference count is never corrupted.

- The object is guaranteed to be destroyed by exactly one thread.

The granularity of the lock managing this is very small.

Yechezkel Mett

unread,
Mar 13, 2007, 2:43:51 PM3/13/07
to
James Kanze wrote:
> So how do you delete an object with dynamic lifetime? I agree
> with regards to delete[], and I don't think I've used it once in
> over 15 years of C++. But delete?
>
> Maybe you meant that all delete should be "delete this"?
...

> In my own applications, I find that most pointers are to objects
> with explicit lifetimes, and I've yet to find a smart pointer
> which is applicable to them (although I've tried).

If the objects are held in a container, explicit delete requires
separate erase and delete, whereas an appropriate smart pointer
(preferably unique_ptr, but absent that, shared_ptr or equivalent will
do) allows the following scenarios (as relevant):

1. Manually ending the lifetime by calling erase (no separate delete needed)
2. Manually ending the lifetime of all contained objects by calling clear
3. Automatically ending the lifetime of all contained objects because
the owner's lifetime has ended

Actually, the third is relevant even without a container; if you have an
object which normally has an explicit lifetime but also has an owner
which it should not outlive (say a subwindow in a GUI). Of course, if
ending the lifetime can fail another solution is required, but that's a
different story.

Yechezkel Mett

James Kanze

unread,
Mar 14, 2007, 11:36:49 AM3/14/07
to
On Mar 13, 7:43 pm, Yechezkel Mett <yechez...@emailaccount.com> wrote:
> James Kanze wrote:
> > So how do you delete an object with dynamic lifetime? I agree
> > with regards to delete[], and I don't think I've used it once in
> > over 15 years of C++. But delete?

> > Maybe you meant that all delete should be "delete this"?
> ...
> > In my own applications, I find that most pointers are to objects
> > with explicit lifetimes, and I've yet to find a smart pointer
> > which is applicable to them (although I've tried).

> If the objects are held in a container, explicit delete requires
> separate erase and delete,

Not exactly. The destructor of the object calls the erase.
It's called not having the tail wag the dog. And avoiding
obfuscation. It also works if the object can be reached via
several containers---the destructor removes it from all of the
containers. (Typically, in fact, the observer pattern is used.
Other objects may have pointers to this object in containers,
which the object itself doesn't know about.)

> whereas an appropriate smart pointer
> (preferably unique_ptr, but absent that, shared_ptr or equivalent will
> do) allows the following scenarios (as relevant):

> 1. Manually ending the lifetime by calling erase (no separate delete needed)

A bit confusing, and definitly not what it wanted. It is the
fact that the object ceases to exist that triggers its removal
from the containers, not the reverse.

> 2. Manually ending the lifetime of all contained objects by calling clear

Definitly not what it wanted.

> 3. Automatically ending the lifetime of all contained objects because
> the owner's lifetime has ended

And we come back to the basic problem: there IS NO OWNER. I do
not want to have to artificially establish an ownership
hierarchy, just to manage object lifetime, when the concept I am
modeling doesn't have one.

> Actually, the third is relevant even without a container; if you have an
> object which normally has an explicit lifetime but also has an owner
> which it should not outlive (say a subwindow in a GUI).

If relationships were only that simple. A subwindow in a GUI
doesn't really have an owner; it has a dependency, that has to
be respected (and thus, it should be an observer of the
containing components). But there are a lot of other cases
where you might want to destruct it, and at least in the GUI I
wrote, it was not unusual to remove a subwindow from the
containing window and keep it around, even after the containing
window had disappeared.

> Of course, if
> ending the lifetime can fail another solution is required, but that's a
> different story.

By definition, ending the lifetime can't fail:-). Delete is a
one-way street in C++, and once you've started it, there's no
going back. But of course, at a higher, logical level, it may
be necessary to treat the case. The function which calls
"delete this" may decide in the end that the object isn't
deletable, and not call delete this. Which means that the
deletion notifications don't propagate from the destructor
(since the destructor isn't called), the object isn't removed
from its container, etc.

--
James Kanze (GABI Software) email:james...@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

--

0 new messages