Never ever use a raw pointer when a smart pointer can do the same job

J

James Kanze

* James Kanze:
It would be nice if objects that may encounter "unrecoverable
failures" were only accessible via such smart pointers.
The smart pointer would do all the validity checking, e.g.
instead as with current iostreams where every iostream
operation checks the state and possibly chooses to do nothing,
the smart pointer would centralize the checking and throw an
exception.

I more or less agree, although I'm not sure that the results
would be considered a "smart pointer". Maybe a decorator. (The
sentry objects are nice, as far as they go, but they really
don't go far enough. In my xdrstream code, I used some small
helper objects for accessing bytes; not only did they ensure
that the error codes were correctly set, they also allowed me to
use intelligible names, instead of sgetc (which only peeks, and
doesn't get) and such:).
But whereas I once saw GUI widgets (which are generally
self-destroying) as a main usage for such a pointer, I now
hold the view that they're better accessed simply via raw
pointers. For the cases where a checking smart pointer would
detect access after destruction, are very few. In the majority
of cases, for a GUI widget the checking smart pointer would
just be added complication.

Once again, I totally agree.
However, files and socket objects and such, I think that's the
proper usage.

But why pointers? In practice, my file and socket objects all
either have local scope, or are part of a bigger object which
either has local scope or a specific lifetime (or in exceptional
cases, like a logger, has static lifetime).
For in contrast to GUI widgets these kinds of objects suffer
from having natural zombie states, states where nearly all
operations are invalid. A checking smart pointer can then
centralize and enforce the validity (non-zombie) checking.

OK. That's an interesting idea.
 
N

Noah Roberts

James said:
BGB said:
Victor Bazarov wrote:
Hicham Mouline wrote:
... my colleague says.
[.,..]
when doing custom memory management; when working with data
which is "not an object" (loosely structured or unstructured
regions of memory, ...); ...
basically, not all tasks involve "objects", sometimes the
task may involve something with far less "identity" or
"structure"...
AKA: raw pointers, for raw memory...
std::vector<char>

Very few cases when that isn't completely appropriate for that
need.

Implementing std::vector:). Or ::eek:perator new.

There are very few cases where raw memory is relevant at all,
and all of the ones I know are at such low levels that
std::vector wouldn't necessarily be available.

As I see it:

-- At the application level, you're not doing that much dynamic
allocation anyway, and when you do, it's generally because
the object has a specific lifetime, so smart pointers (at
least the usual ones) aren't all that useful. Most, if not
all, pointers are for navigation only, and raw pointers do
that job very well.

I don't know what you work on but I disagree with everything you just
said. I'm having a hard time even figuring out why you think any of
that is true. And to think I spent a lot of effort and time responding
directly to your statements...all without a single address, you just
repeat what you said before. I don't know why you think any of it's true.

Not do much dynamic allocation at the application level? What can you
possibly mean by "application level" where that is the case?

Smart pointers aren't useful when dealing with objects that have
specific lifetimes? Since when is this true?

Raw pointers do just fine for navigation only? I explained several
reasons why this is not something one should assume, do you care to
respond to any of them?

This is why I find even trying to discuss things with you so
frustrating. I try to give you benefit of doubt, you're not an
idiot...but some of the things you say...wtf? You seem to just say off
the wall stuff and expect everyone to agree, and ignore it when someone
explains why you're probably wrong. Next you'll probably start saying,
once again, that you're the only one that can have worked on "good" code
or some equally irrelevant nonsense.

BAH! Just keep repeating it. I'm done.
 
I

Ian Collins

Noah said:
James said:
BGB / cr88192 wrote:
Victor Bazarov wrote:
Hicham Mouline wrote:
... my colleague says.
[.,..]
when doing custom memory management; when working with data
which is "not an object" (loosely structured or unstructured
regions of memory, ...); ...
basically, not all tasks involve "objects", sometimes the
task may involve something with far less "identity" or
"structure"...
AKA: raw pointers, for raw memory...

Very few cases when that isn't completely appropriate for that
need.

Implementing std::vector:). Or ::eek:perator new.

There are very few cases where raw memory is relevant at all,
and all of the ones I know are at such low levels that
std::vector wouldn't necessarily be available.

As I see it:

-- At the application level, you're not doing that much dynamic
allocation anyway, and when you do, it's generally because
the object has a specific lifetime, so smart pointers (at
least the usual ones) aren't all that useful. Most, if not
all, pointers are for navigation only, and raw pointers do
that job very well.

I've been avoid this particular cat fight, but because I agree with both
of you, I'll pile in!
I don't know what you work on but I disagree with everything you just
said. I'm having a hard time even figuring out why you think any of
that is true. And to think I spent a lot of effort and time responding
directly to your statements...all without a single address, you just
repeat what you said before. I don't know why you think any of it's true.

We all do that form time to time...
Not do much dynamic allocation at the application level? What can you
possibly mean by "application level" where that is the case?

I'd like to see that one cleared up as well.
Smart pointers aren't useful when dealing with objects that have
specific lifetimes? Since when is this true?

For ever?

Note James qualified his broad statement with "(at least the usual ones).
Raw pointers do just fine for navigation only? I explained several
reasons why this is not something one should assume, do you care to
respond to any of them?

I'm in the yes and no camp on this one, most of my use of naked pointers
it to iterate through things. The things being iterated over may not by
dynamic objects. I think that point has been lost in the noise -
pointers don't always point to dynamic objects.

But I also do a lot of work with dynamic input data, where most pointers
used will be for dynamic objects. Although in these cases, the pointers
will not be visible to the "application code", they will be contained
within the objects managing the dynamic memory.

Maybe James' point is "Most, if not all, *naked* pointers are for
navigation only"?

I am a big smart pointer fan and user, they were one of the first Wow!
things I discovered when I learned C++ way back when. However, like any
tool they can be over used.

As for the original claim, "Never ever use a raw pointer when a smart
pointer can do the same job", it is nonsense.
 
J

Juha Nieminen

Noah said:
Second off, note that no raw pointer is exposed by std::list.

I didn't say "I would like to see him *use* a doubly-linked list with
smart pointers."

What I said was "I would like to see him *implement* a doubly-linked
list with smart pointers."

There's a radical difference. Your argument that std::list doesn't
expose the raw pointers is about *using* std::list rather than
*implementing* it.
 
J

James Kanze

All of the above allow you to delete the object explicitly.

Do they? boost::shared_ptr certainly doesn't. And the other
two aren't legal in containers: you can't have a map< IdType,
std::auto_ptr >, for example.

IIUC, you will be able to have a map< IdType, std::unique_ptr >
in the next release of the standard. I still feel that most of
the time, the logical and intuitive way of handling the issue is
the reverse: you remove the object from the map in its
destructor, rather than removing it from the map in order to
destruct it. Not to mention the complications involved when
transactional semantics apply: in case of a roll-back, you have
to "undelete" the object (which of course means that you can't
actually have deleted it, in the C++ sense---you've just put a
pointer to it in a buffer in the transaction, and the
transaction will delete it in the commit).
That is almost certainly a required feature of any smart
pointer that is involved with resource ownership.

Apparently, the people who developed the boost::smart_ptr don't
agree with you. (FWIW, my reference counted pointer doesn't
support this either.)
That's certainly not true. There are numerous reasons to
dynamically allocate something that is still tied to some kind
of scope.

Certainly. Polymorphism comes immediately to mind. But are
they that frequent?
For instance:
void do_something_with_chars(??)
{
std::vector<char> buffer(some calculated size);
... do stuff ...

This is better than using new to create an array because "...
do stuff ..." could be exception generating code. It's just
plain easier to write exception safe code with RAII and, in
fact, sometimes it's the only way.

I totally agree, but are you saying that std::vector is a smart
pointer?

Perhaps I should have been clearer: at the application level, I
find it fairly rare to dynamically allocate objects with a
lifetime which corresponds to scope. I don't consider using
std::vector or std::string dynamically allocating objects at the
application level---what goes on behind the scenes doesn't
interest me (provided they works, and the profiler doesn't say I
have to take an interest). When I think "smart pointer" related
to memory or lifetime management, I think of something like
boost::shared_ptr, where the client code does the actual
allocation.
Not in my experience. You certainly don't use it naively but
I've seen no reason to believe a gc would do the job better.
For one thing a gc isn't often deterministic to the
application code

Nor is boost::shared_ptr, unless you're keeping track of all
those pointers yourself. Which sort of defeats the purpose of
it.

As a poor man's garbage collection, used in specific cases where
it is known that no cycles can occur, it actually works pretty
well. But true garbage collection requires even less programmer
effort in such cases, and is generally faster. (When it's
available. I've used the Boehm collector, but I won't pretend
that installing it on an exotic platform is something for a
beginner.)
and thus RAII isn't even possible (and RAII has many uses
beyond working with memory resources, it can be used to guard
any allocatable resource).

Certainly. And for a lot of resources (mutex locks, open file
descriptors, etc.), it's often appropriate---most of the time
(but not always), such resources either have a lifetime which
does correspond to a local scope, or (at least in the case of
open file descriptors) have a lifetime which corresponds to some
other, "managed" object. But in such cases: why the pointer? I
wouldn't consider something like boost::scoped_lock a smart
pointer, for example.
Yes I forgot about that one. I mentioned a couple exceptions
but that wasn't in the list. There is of course a way around
even this though and in many cases is the better answer: null
object.

Whether it is a better answer depends a lot on the situation.
I've used null objects at times, but they're often more work to
implement and less transparent to the reader than a null pointer
and a test.
Covered in () by a.

OK. I was thinking of the most frequent use of pointers in my
code: for navigation. If the data structure is at all dynamic,
the pointers come and go, and change. And since it's such a
frequent use, I'd question your use of "only" in the above; is
it really appropriate to say "the only case" when one of the
cases covers 90% of the uses?
Desktop applications.

That's sort of vague, but it does sound like something I've no
experience in---the only time I wrote software for a desktop was
when I implemented a GUI front-end; it certainly wasn't a
stand-alone application. (It was also in Java, but off hand: I
don't seem many cases where I'd use things like shared_ptr, but
unlike the other applications I've worked on, I can see
arguments both ways about that. Almost everything is in a
cycle, but there is a very distinct containment hierarchy which
should make it absolutely clear where you need weak_ptr.)
Even when pointers are used "only for navigation" it is of
benefit to use something like a shared_ptr.

This is where I disagree. If you want to use shared_ptr, you
need to manage some sort of zombie state. And in the absence of
any strict hierarchy (like the containment hierarchy of a GUI),
handling cycles is far too error prone. (And cycles there
are. Lot's of them.)
The problem with pointers that are "only for navigation" is
that the objects that have them are dependent upon the
lifetime of other objects and thus are intimately coupled with
them...but they often don't even know about them.

You've confused me a little with too many "they". The object
which contains the pointer certainly knows that it has the
pointer. And in practice, the pointed to object has to know as
well. I've got a smart pointer which manages this on my
(inaccessible) site---sort of a weak_ptr, but without the
shared_ptr. But in practice, I've almost never used it, because
not only does the pointer need to know when the pointed to
object is destructed, but the object which contains the pointer
needs to know as well (e.g. to switch to a back-up resource, or
an alternative strategy, or to remove the pointer from a map or
a set or something). In the end, you almost always need some
variant of the observer pattern.

(When I first started C++, there was a big fad for "relationship
managemend", some sort of generic solution to this
problem---which is very real. The fad passed on, without any
real working solution being found, at least to my knowledge.)
I do sometimes ignore this problem but I often regret that
choice. A shared_ptr is not usually an expense that
overweights the expense of debugging a dangling pointer.

The problem is that the way the shared_ptr prevents the dangling
pointer is by violating the basic premises of the system---that
the object is deleted when the external event says it has to be
deleted. You could probably use a shared_ptr in the object
itself (pointing to the object, and reset by the object when it
received the event terminating its lifetime), and weak_ptr
everywhere else, but that really comes down to my ManagedPtr,
and as I said, I've not found it anywhere near as useful as I'd
hoped.

IMHO, the dangling pointer isn't the problem; it's a symptom of
a larger problem. The real problem is that object A, which
needs object B, wasn't informed that object B ceased to exist,
and so wasn't able to take appropriate action. And avoiding the
dangle will often only serve to hide this larger problem, or
make it harder to find. I regularly replace the global operator
new with a version which overwrites all of the freed memory with
something like 0xDEADBEEF; unless the memory has been
reallocated since the delete, any access through the dangling
pointer is going to cause a core dump, very quickly. (If the
object is polymorphic, immediately, since the vptr was
overwritten. And one of the things I like about garbage
collection in this scenario is that it ensures that the memory
of the destructed object will not be reallocated as long as
there is a pointer to it.)
There are of course times when you actually would prefer a
dangling pointer to an object outliving what is conceived as
its useful lifetime.

Let's formulate that a little differently: are there times whey
you wouldn't prefer a dangling pointer over using an object
whose defined lifetime has ended?
Either way you look at it you probably have a bug on your
hands when an object that has a "navigation only" pointer
exists longer than the object owning the thing it points to.
Exactly.

However, often times the path to recreate this bug is
difficult to recreate and if just having the thing do weird
stuff the user never sees is preferable to a crash.

OK. Maybe that's where our applications differ. Having the
application do "weird stuff" is generally not an option in the
applications I've written. In most cases, it's better for the
application to crash, immediately, so the backup systems (in the
current case, doing the operations manually, or at least knowing
that the system is not performing correctly, and working around
it) can take over.
And designs change. It's easier to say, "Now I want this
object to live longer," if the pointers it's using are shared.

Having the object live longer usually means a major design
change, at least for the objects I have to deal with.
Further, you actually have ways to have a "navigation only"
pointer that can be checked before you access it. In the case
of a raw pointer you have to trust that it is either there, or
has been set to NULL. If both sides are using the shared_ptr
system, one the owner (having a shared_ptr) and the other the
referrer (having a weak_ptr) then when the owner goes away we
know that everyone having a "navigation only" pointer to
whatever parts of it it's shared know the resource is gone.

Yes. That's what my ManagedPtr does (except that there's no
shared_ptr involved, so the abstraction is clear). As I said,
from experience, it isn't that useful. The object with the
pointer generally needs to take some explicit action when the
pointed to object is destructed.
Of course, these are all things that any good developer takes
care of 100% of the time, right? Nobody ever leaves a
dangling pointer or neglects setting something to NULL...

Well, that's why you have code reviews:). And extensive
testing, with an instrumented system (Purify or valgrind, at the
least, a debugging operator new and operator delete, etc.).
It is true that really rigerous practices and good developers
that are very observant all the time might be able to get it
right all the time.

No one person gets it right all of the time. A well organized
team can get it right almost all of the time. And using
instrumentation like valgrind during testing can catch most of
the cases they miss, provided your test suites are thorough
enough. And using a garbage collector can ensure that the
memory which has been freed will not be reused as long as that
dangling pointer exists.
In my experience that's just too much to ask even of myself.
I **** up sometimes but if I'm using a device that renders my
fuckups nil or impossible it definitely makes my job easier on
both ends, coding and debugging.

Certainly, if such a device existed. But in my experience,
shared_ptr and its brethren, indiscriminately used (there are
cases where its appropriate), doesn't really reduce the
probability of fuckups; it just hides them in a way that makes
them more difficult to find.
Then you have the cases when your "navigation only" pointer
points at a stack allocated object.

That's actually very, very rare. Practically speaking, you can
only navigate between objects which have identity. (Copying an
object will screw up navigation tremendously.) And it's rare
(but not non-existant) for such objects not to be dynamically
allocated.
Believe it or not you can still use smart pointers here. Hold
a boost::shared_ptr to your local stack object (and you can
just stick these in a boost::any array if you want), return
weak_ptr objects from your accessor method. Granted, easily
broken so there's probably a better way (and I don't
necessarily recommend that method) with some smart pointer
object of your own but the idea is that everyone who has a
pointer now has some method of checking that the navigation is
still available before using it.

We're back to my ManagedPtr:).
On top of all that though there are many practices that create
dynamically generated objects that are totally reliant on
scope. My array example is one, pimpl (or handle/body) is
another and is one we use a lot.

Handle/body is definitely one, but 1) it's pretty easy to get
right without the smart pointer, and 2) the smart pointer you
want (boost::scoped_ptr) doesn't work in this case, since it can
only point to a completely defined type. You could use
shared_ptr for a pimpl, but I'd argue that that gives the wrong
message, since the pointed to object certainly isn't shared in
any way. Still, if there was a smart pointer with the basic
semantics of scoped_ptr, but which allowed instances in contexts
where the targetted object wasn't fully defined, like
shared_ptr, I'd buy it. (I suspect that scoped_ptr will
actually work here, *if* you don't forget to make the destructor
explicit, and implement it in the source file, where the impl
class is known. But it's not guaranteed, and I'd rather prefer
that the code refuse to compile if the destructor of the
scoped_ptr were instantiated in a context where the pointed to
type was not complete. All of which is probably doable.)
There are other methods one can take to respond to these
problems but I find expecting all pointers to be some form of
"smart" pointer object is the most straightforward, easiest to
check in code reviews, and objectively enforceable as a
standard. Any time you see a raw pointer you ask, "Why did
you do that?" Then you all have the option of designing a
smart pointer that addresses the particular problem that no
other did...or live with the raw pointer.

And I find the rule too constraining, given that most of my
pointers are for navigation, and most of the time, the owner of
the pointer needs to be registered as an observer anyway, for
other reasons, as well as in order to get rid of the pointer.

I also worry that the rule will cause the programmers to start
using shared_ptr everywhere, when it is rarely the appropriate
solution. With a large enough choice of smart pointers, it's
less of a problem, but for far too many programmers I've met,
smart pointer means boost::shared_ptr. And of course, there is
also a certain number of programmers who will look on the smart
pointer as a silver bullet, and stop thinking about the real
issues of object lifetime and notification; education and code
review should be able to solve those problems, but then, with
education and code review, the use of raw pointers ceases to be
a problem as well.
 
J

James Kanze

James said:
BGB / cr88192 wrote:
Victor Bazarov wrote:
Hicham Mouline wrote:
... my colleague says.
[.,..]
when doing custom memory management; when working with data
which is "not an object" (loosely structured or unstructured
regions of memory, ...); ...
basically, not all tasks involve "objects", sometimes the
task may involve something with far less "identity" or
"structure"...
AKA: raw pointers, for raw memory...
std::vector<char>
Very few cases when that isn't completely appropriate for that
need.
Implementing std::vector:). Or ::eek:perator new.
There are very few cases where raw memory is relevant at all,
and all of the ones I know are at such low levels that
std::vector wouldn't necessarily be available.
As I see it:
-- At the application level, you're not doing that much
dynamic allocation anyway, and when you do, it's
generally because the object has a specific lifetime, so
smart pointers (at least the usual ones) aren't all that
useful. Most, if not all, pointers are for navigation
only, and raw pointers do that job very well.
I don't know what you work on but I disagree with everything
you just said. I'm having a hard time even figuring out why
you think any of that is true. And to think I spent a lot of
effort and time responding directly to your statements...all
without a single address, you just repeat what you said
before. I don't know why you think any of it's true.
Not do much dynamic allocation at the application level? What
can you possibly mean by "application level" where that is the
case?

Well, I guess it does sort of depend on what you mean by
application level. In another posting, you mention the pimpl
idiom, which is certainly a case; it's become so automatic with
me for complex objects that I tend to forget that it is explicit
dynamic allocation. But in general, and I think you agree,
objects with value semantics aren't allocated dynamically, and
entity objects tend to have explicit lifetimes, which
correspond to external events, and not to any particular scope.
Smart pointers aren't useful when dealing with objects that
have specific lifetimes? Since when is this true?

They're not that useful. Typically, an object which reacts to
an event which terminates its lifetime will either do a delete
this, or it will register with the current transaction, which
will delete it in the commit. I don't see where smart pointers
would play much of a role here---in the case of "delete this",
"this" can't be a smart pointer.
Raw pointers do just fine for navigation only? I explained
several reasons why this is not something one should assume,
do you care to respond to any of them?

I did, in the posting where you explained them. I read (and
respond to) the postings one at a time.
This is why I find even trying to discuss things with you so
frustrating.

The fealing is mutual, but when we both try, I learn something.

Look, I don't have you in front of me when I'm writing, so I
can't immediately see when you don't see what I'm trying to say.
And it's not easy to try to explain something that is "obvious"
to me, based on my experience, to someone who doesn't see it,
because I don't immediately see why it isn't obvious.
I try to give you benefit of doubt, you're not an idiot...but
some of the things you say...wtf? You seem to just say off
the wall stuff and expect everyone to agree, and ignore it
when someone explains why you're probably wrong.

I will use rhetoric at times. Particularly, when someone makes
a blanket statement which I find obviously wrong, I'm wont to
make a blanket statement to the opposite effect. I've tried
smart pointers---in one application, I tried to make every
pointer a smart pointer, and it almost destroyed the
application; I had to go back, very rapidly, and make most of
them raw pointers.
Next you'll probably start saying, once again, that you're the
only one that can have worked on "good" code or some equally
irrelevant nonsense.
BAH! Just keep repeating it. I'm done.

Yes, it's easier to say that than to explain why you disagree.
(Note that I've never said that I'm the only one who's worked on
"good" code, and that the context when I spoke of "good code"
was completely different---there was some idiot claiming that
I'd never known a really good programmer.)
 
J

James Kanze

Noah Roberts wrote:
I've been avoid this particular cat fight, but because I agree
with both of you, I'll pile in!

FWIW: I agree with both of us, too, but I've never learned much
by just saying "you're more or less right". But I really do
think that too much force is being given to smart pointers.
They're a useful tool in certain cases, but they're not a silver
bullet, and there are a lot of cases where they don't really buy
you much. At least not the smart pointers I've seen to
date---the issue when pointers are used for navigation is
relationship management, and to date, I'm not aware of any good,
usable generic solutions. Every relationship, needs to be
solved on its own. (The observer pattern is often a good start,
but a pattern is still a long way from actual code.)

[...]
For ever?
Note James qualified his broad statement with "(at least the
usual ones).

I suspect that it's the word "specific" which is ambiguous here.
What I meant is that objects whose lifetime is more or less
determined by external events, or the specifications, or
something specific of that nature. In other words, and object
which can't just disappear because there are no more pointers to
it (at least in the application code---it might still be
registered somewhere in the system as an event handler), and
objects which must disappear when the program logic says they
have to disappear, even if there are outstanding pointers to
them (which is probably---almost certainly, in fact---a
programming error).
I'm in the yes and no camp on this one, most of my use of
naked pointers it to iterate through things. The things being
iterated over may not by dynamic objects. I think that point
has been lost in the noise - pointers don't always point to
dynamic objects.

Do you mean like the cases where std::vector<>::iterator is a
pointer? Or that there are pointers hidden inside some
iterator (which isn't really what I would consider application
level)? Or that you run around linked lists, trees, etc. at the
application level? (Some applications doubtlessly do.)
But I also do a lot of work with dynamic input data, where
most pointers used will be for dynamic objects. Although in
these cases, the pointers will not be visible to the
"application code", they will be contained within the objects
managing the dynamic memory.
Maybe James' point is "Most, if not all, *naked* pointers are
for navigation only"?

No. What I'm trying to say is that most things that would be
considered a pointer at the application level (naked pointers or
smart pointers) are used for navigation (and thus should usually
be naked pointers, because the smart pointers I've seen don't
buy you anything but trouble when used for navigation).
Certainly, the application uses a lot of other pointers, but not
directly---I don't consider std::vector a smart pointer, for
example, or std::map.
I am a big smart pointer fan and user, they were one of the
first Wow! things I discovered when I learned C++ way back
when. However, like any tool they can be over used.

Exactly. I was a really big fan of them until I got an
opportunity to use them exclusively---basically, to apply the
suggested rule that all pointers should be smart pointers. And
found that it just got me into trouble in a lot of cases.
As for the original claim, "Never ever use a raw pointer when
a smart pointer can do the same job", it is nonsense.

And vice versa. What I really started out by saying is that
both are important tools to have in your tool kit, and you need
to understand when each is important.
 
I

Ian Collins

James said:
They're not that useful. Typically, an object which reacts to
an event which terminates its lifetime will either do a delete
this, or it will register with the current transaction, which
will delete it in the commit. I don't see where smart pointers
would play much of a role here---in the case of "delete this",
"this" can't be a smart pointer.
James, I think your "Typically," is the cause of most of the
misunderstanding on this thread.

The idiom you describe isn't one I have used, I don't think I have ever
written "delete this" in anger. My typical scenario is an object is
created, it gets placed in a container for later processing and it gets
erased from the container once it has been processed.

In that scenario, a reference counted smart pointer is the easiest way
to manage the lifetime of the object.
 
I

Ian Collins

James said:
Do you mean like the cases where std::vector<>::iterator is a
pointer? Or that there are pointers hidden inside some
iterator (which isn't really what I would consider application
level)? Or that you run around linked lists, trees, etc. at the
application level? (Some applications doubtlessly do.)

I mean cases where the code has to traverse something in memory that
isn't neatly packaged in a C++ style container.

A basic example would be scanning data in a file, or copying data from
one place to another.

One term you should quantify is "application level". You use it often
and I'm not sure where you draw the line.
No. What I'm trying to say is that most things that would be
considered a pointer at the application level (naked pointers or
smart pointers) are used for navigation (and thus should usually
be naked pointers, because the smart pointers I've seen don't
buy you anything but trouble when used for navigation).

I agree, with the exception of container iterators.
Certainly, the application uses a lot of other pointers, but not
directly---I don't consider std::vector a smart pointer, for
example, or std::map.

True. But they do share one attribute with smart pointers: they manage
dynamic memory. While I disagree with the assertion in the subject, I'm
a firm believer in navigation (the pointer is initialised to point to an
existing object) being the only use for naked pointers in C++ code. The
only time I'd expect to see "new" would be in the initialisation of a
smart pointer or for an object to be inserted in a managed container.
And vice versa. What I really started out by saying is that
both are important tools to have in your tool kit, and you need
to understand when each is important.

I don't think anyone will disagree with that.
 
K

Keith H Duggar

Ah, but those are smart pointers:). Not the type Noah was
thinking of, I think, but they're certainly more like smart
pointers than they are iterators. (In normal usage, "random
access iterator" is an oxymoron.)

Can you please expound upon 'In normal usage, "random access
iterator" is an oxymoron.' It seems like an interesting point
and I'm afraid I'm not quite seeing it entirely. Is it simply
that "iteration" in "normal" usage perhaps implies monotonic
sequencing? Or is there something deeper?

KHD
 
K

Keith H Duggar

But why pointers? In practice, my file and socket objects all
either have local scope, or are part of a bigger object which
either has local scope or a specific lifetime (or in exceptional
cases, like a logger, has static lifetime).


OK. That's an interesting idea.

So such socket smart pointers have less to do with managing
lifetime and more to do with checking validity and setting/
throwing appropriate errors? Indeed that's very interesting.

KHD
 
K

Keith H Duggar

I do sometimes ignore this problem but I often regret that
choice. A shared_ptr is not usually an expense that
overweights the expense of debugging a dangling pointer.

The problem is that the way the shared_ptr prevents the dangling
pointer is by violating the basic premises of the system---that
the object is deleted when the external event says it has to be
deleted. You could probably use a shared_ptr in the object
itself (pointing to the object, and reset by the object when it
received the event terminating its lifetime), and weak_ptr
everywhere else, but that really comes down to my ManagedPtr,
and as I said, I've not found it anywhere near as useful as I'd
hoped.

IMHO, the dangling pointer isn't the problem; it's a symptom of
a larger problem. The real problem is that object A, which
needs object B, wasn't informed that object B ceased to exist,
and so wasn't able to take appropriate action. And avoiding the
dangle will often only serve to hide this larger problem, or
make it harder to find. I regularly replace the global operator
new with a version which overwrites all of the freed memory with
something like 0xDEADBEEF; unless the memory has been
reallocated since the delete, any access through the dangling
pointer is going to cause a core dump, very quickly. (If the
object is polymorphic, immediately, since the vptr was
overwritten. And one of the things I like about garbage
collection in this scenario is that it ensures that the memory
of the destructed object will not be reallocated as long as
there is a pointer to it.)
[snip]
In my experience that's just too much to ask even of myself.
I **** up sometimes but if I'm using a device that renders my
fuckups nil or impossible it definitely makes my job easier on
both ends, coding and debugging.

Certainly, if such a device existed. But in my experience,
shared_ptr and its brethren, indiscriminately used (there are
cases where its appropriate), doesn't really reduce the
probability of fuckups; it just hides them in a way that makes
them more difficult to find.

Yes, that is one crucial problem with blanket shared_ptr use and
also with Java, for example, where effectively every UDT object
is managed by a GC controlled shared pointer. (Please note, I am
not saying this is GC related. I remember very well the valuable
lesson you taught me back in

http://groups.google.com/group/comp.lang.c++/msg/63608953022b0a3a

that GC and object lifetime need not be coupled; and that GC can
provide valuable error catching tools as in the DEADBEAF example
you mention above.)

KHD
 
K

Keith H Duggar

I totally agree with everything you just said. Simply slapping a smart
pointer on a raw pointer without thinking is not a good approach. You
should think about what the raw pointer is for and then analyze what
construct would best smarten it up.

On the other hand, it's not going to harm anything to do it.

Wrong. Changing adapter to auto_ptr, shared_ptr, etc here in this
case could cause a disaster. our_adapter has static lifetime while
*adapter destruction is currently controlled by a call to finalize.

Linking the destruction order of *adapter to the static lifetime of
adapter can introduce a range of order of destruction problems. For
example, for all we know finalize() is currently being called during
the destruction of another static object at the end of a translation
unit in which other static object cleanups requires the *adapter.
Now we have undefined behavior.

Of course, there are ways to fix this. But, the point is that in
even this simple example, the "rule" could have easily induced a
code breaking change.

KHD
 
K

Keith H Duggar

I think there's some misunderstanding of what a standard like this
should look like by some posting and maybe by your boss as well. It is
'generally' impossible to use smart pointers for everything and not
desired all the time either.

(just-kidding(

Hmm ... interesting you changed "colleague" to "boss". I wonder if
Noah is Hicham's boss and Hicham is trying to get even with Noah for
his "dealing with lower level programmers" thread by posting one of
Noah's "rules" for the world to see ;-)

)just-kidding)

KHD
 
J

James Kanze

One term you should quantify is "application level". You use
it often and I'm not sure where you draw the line.

Me neither. In many ways, it's intentionally vague. As are all
the terms like "usually" or "in most cases". There are no hard
and fast rules, and there is no strict line. By application
level, I mean something along the lines of code which deals with
application level abstractions, things like business logic, or
call handling; things you'd name using names from the
application domain. But it's not a hard and fast line: in most
programs, a class managing an HTTP connection would be part of
the infrastructure, but in a browser or a web server?
I agree, with the exception of container iterators.

Yes. In the measure that STL-style iterators are smart
pointers, you'll use that type of smart pointer at the
application level. But I suspect that I'm in a minority in
considering STL iterators smart pointers, and conceptually, when
I use them, the higher level abstraction that I'm concerned with
isn't a pointer. (In general, when I provide the iterator, it
also provides a GoF interface, which is closer to the
abstraction I'm generally using.)

The issue doesn't have any absolute answer, precisely because
there are so many variables, delimited by vague terms.
True. But they do share one attribute with smart pointers:
they manage dynamic memory.

But that's irrelevant, since they don't provide the interface or
the services of a pointer.
While I disagree with the assertion in the subject, I'm a firm
believer in navigation (the pointer is initialised to point to
an existing object) being the only use for naked pointers in
C++ code.

I wouldn't go so far, but in practice, in a lot of applications,
it doesn't matter, since navigation is often just about the only
use of pointers, smart or otherwise. Except, of course, the
this pointer, but you can't use a smart pointer there.
The only time I'd expect to see "new" would be in the
initialisation of a smart pointer or for an object to be
inserted in a managed container.

Really. What about entity objects, which manage themselves.
Often (at least in the code I've seen), the results of the new
expression aren't used at all---it's just:
new MyType( someArguments ) ;
, with the value of the new expression being discarded. When
this isn't the case (which is also fairly often---it depends on
the requirements of the code), the results of new generally will
be used to initial a smart pointer, almost always std::auto_ptr.
But once the necessary arrangements have been made so that you
can navigate to the object, or find it when you need it, which
is generally before the end of the transaction, release() is
called on the auto_ptr. It's a temporary measure, covering you
until all of the proper invariants can be established.
I don't think anyone will disagree with that.

So why such a reaction to my original comment:) ?
 
J

James Kanze

Can you please expound upon 'In normal usage, "random access
iterator" is an oxymoron.' It seems like an interesting point
and I'm afraid I'm not quite seeing it entirely. Is it simply
that "iteration" in "normal" usage perhaps implies monotonic
sequencing? Or is there something deeper?

Not really. And "normal usage" probably isn't the best choice
of words. But traditionally, in pre-STL days, an iterator was a
means of visiting each element in a collection or a sequence
once, and exactly once. Possibly in some pre-defined order,
possibly not. Once you support random access, and things like
operator[], what you really have is a view.

At least, that's the terminology that was current in the circles
I frequented. Before the STL. Of course, terminology changes,
and what I called a view, above, I might now call the decorator
pattern:). And when it's clear that I'm talking about C++,
well... in C++, the word "iterator" means exactly what the
standard chooses it to mean. (I don't mean that pejoritively;
in many ways, the standard has to take this route, in order to
be as precise as possible.)
 
J

James Kanze

I totally agree with everything you just said. Simply
slapping a smart pointer on a raw pointer without thinking is
not a good approach. You should think about what the raw
pointer is for and then analyze what construct would best
smarten it up.
On the other hand, it's not going to harm anything to do it.

It may be unnecessary added complexity. I'll grant you that it
does provide a hook, a place where you can add extra checking if
needed. And could prevent some really stupid mistakes, like
e.g. incrementing a pointer which doesn't point into an array.
But such more or less transparent smart pointers don't seem to
be common; for better or worse, when people speak of "smart
pointers", they are almost always thinking of Boost::shared_ptr.

If your project actually has such smart pointers (e.g. a
navigation_ptr, or whatever, that doesn't have anything to do
with object lifetime management, other than, perhaps, checking
that the object is still alive when the pointer is
dereferenced), and your coding guidelines say to use these
(amongst other smart pointers), then I take back my objection to
them. My impression is, however, that when people propose rules
like "never use a raw pointer", this isn't what they have in
mind (but I'd be happy to be proven wrong).
If they're trying to get you to adhere to a standard in
development then even enforcing it in cases like this, though
they may seem pedantic and stupid, might be the best move on
their part.
In fact, one case an auto_ptr would help is if initialize is
called multiple times. Your code never actually checks to see
if it already exists, ergo you could get a memory leak if a
client program called the function repeatedly. An auto_ptr
would delete the previous reference and then accept the new
one...no leak.

But a loss of identity. This might be acceptable for an
immutable object, but even then, I'd prefer something more on
the lines of a singleton. (And of course, even if immutable, if
the value depends on some arguments to the constructor, if you
construct it twice, you might want to check that the arguments
are identical.)
 
J

James Kanze

I think there's some misunderstanding of what a standard like
this should look like by some posting and maybe by your boss
as well. It is 'generally' impossible to use smart pointers
for everything and not desired all the time either.
For example, try implementing a smart pointer without a raw
pointer. Sounds pretty stupid, doesn't it. Nieminen makes a
fairly similar demand though for it's a logical end of
"never". Although a linked list can be made with smart
pointers, it may not be the best approach. The more important
thing is to abstract the list in such a manner that you never
know or care that it's using pointers inside...or not.

Hey, we agree about something. At the lowest level, you can't
use smart pointers, because they're not implemented yet.
A better standard might tell you to not pass raw pointers out
of any public interface

I'm less sure about this. *IF* the public interface returns a
pointer to a dynamically allocated object, *AND* expects the
caller to take over lifetime management of this object, then I'd
strongly recommend using an std::auto_ptr for the return value.
But most of my functions which return pointers are more along
the lines of looking up an existing object. Logically, what
they're returning is a Fallible< T& >, a reference and not a
pointer, except that the function can fail to find the object.
And in my book, the "standard" implementation of Fallible< T& >
is T*; my Fallible class doesn't support instantiation over
reference types, and I've never been motivated to add such
support, given that T* works so well. (On the other hand...
fairly recently, I extended the class to handle error
codes---not only failure, but why. I haven't needed this
extension for "references" yet, but if I did, it's obvious that
T* won't suffice. So maybe Fallible< T& > is the correct
solution, even if, compared to a T*, it seems a bit over-complex
and heavy.)
and to prefer smart pointers internally as well. This allows
the developer to use raw pointers internally when they just
make the most sense but then none of this is exposed, which is
where most problems start.

More generally, public interfaces may require stricter
guidelines than internal implementation code. I think that's a
good point, often overlooked.
 
P

peter koch

(Never ever use a raw pointer when a smart pointer can do the same
job)
... my colleague says.

I disagree on the "Never".

Opinions about this "rule of thumb" welcome.

regards,

It really depends on what the job of the pointer is. If the pointer
represents a ressource, I tend to agree with your collegue: a delete
should rarely be placed anywhere else but in a class that is
responsible for the ressource itself. Not doing so might allow
ressources to leak in the case of exceptions or whenever the program
path is changed due to the correction of an unrelated error or an
extension of functionality.
Having said that, naked pointers by themselves are not by any means a
sign of bad design. On the contrary, there are loads of places where
they are appropriate. Too many, in fact: a pointer has far too many
purposes: as an optional value, for iteration, for traversal, to
represent polymorphic objects and so on. It really would be nice if
you could specialise elegantly on some of this, and if you can my
recommendation is to use the specialised version or e.g. use boosts
optional instead of raw pointers.

/Peter
 
J

JustBoo

Noah said:
Next you'll probably start saying, once again, that you're the only
one that can have worked on "good" code or some equally irrelevant
nonsense.

BAH! Just keep repeating it. I'm done.

I figured out a couple of years ago this "place" had become, in almost
every way, just like the Star-Trek Continuum.
http://memory-alpha.org/en/wiki/Q_Continuum

A fantasy-world filled with myopic beings that *never change.*

"Their Belly-Button Lint Collections must be massive, if not galactic
in size."
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top