tags:

views:

1492

answers:

18

I'll start out by saying, use smart pointers and you'll never have to worry about this.

What are the problems with the following code?

Foo * p = new Foo;
// (use p)
delete p;
p = NULL;

This was sparked by an answer and comments to another question. One comment from Neil Butterworth generated a few upvotes:

Setting pointers to NULL following delete is not universal good practice in C++. There are times when it is a good thing to do, and times when it is pointless and can hide errors.

There are plenty of circumstances where it wouldn't help. But in my experience, it can't hurt. Somebody enlighten me.

+15  A: 

I've got an even better best practice: Where possible, end the variable's scope!

{
    Foo* pFoo = new Foo;
    // use pFoo
    delete pFoo;
}
Don Neufeld
Yes, RAII is your friend. Wrap it in a class and it becomes even simpler. Or don't handle memory yourself at all by using the STL!
Brian
Yes indeed, that's the best option. Doesn't answer the question though.
Mark Ransom
This seems to be just to be a by-product of using function scopes period, and doesn't really address the problem. When you're using pointers, you're usually passing copies of them several layers deep and then your method is really meaningless in an effort to address the problem. While I agree that good design will help you isolate the errors, I don't think that your method is the primary means to that end.
San Jacinto
Come to think of it, if you could do this, why wouldn't you just forget the heap and pull all of your memory off the stack?
San Jacinto
@San Jacinto: That's a scope created explicitly to manage the pointer's lifetime, it's unrelated to function scope.
Don Neufeld
I wouldn't say it's unrelated. They follow the same rules. What I'm saying is that you don't gain much except making your code sloppier because you're usually going to be using a pointer deeper in the call stack than this particular scope. In my experience, when I look at someone's pointer problem, it usually isn't even in the same function where they allocated the memory. If it is in the same function, it's usually because the function is TOOOOO LOOOONNG, and your method only encourages this. I find it interesting, but I can't upvote. I don't see the utility.
San Jacinto
Code evolves. Someone will eventually add code in that scope but after the delete. (The new code may need to be inside the block rather than after it to get access to other variables that live only at that scope.) In that case, it'll be better if you set `pFoo` to `nullptr` (or `NULL`).
Adrian McCarthy
@Don, if the use of the pointer variable is confined to a scope, then just use stack allocated variable.
Jagannath
My example is intentionally minimal. For example, instead of new, maybe the object is created by a factory , in which case it can't go on the stack. Or maybe it's not created at the beginning of the scope, but located in some structure. What I'm illustrating is that this approach will find any misuse of the pointer at **compile time**, whereas NULLing it will find any misuse at **run time**.
Don Neufeld
+4  A: 

If there is more code after the delete, Yes. When the pointer is deleted in a constructor or at the end of method or function, No.

The point of this parable is to remind the programmer, during run-time, that the object has already been deleted.

An even better practice is to use Smart Pointers (shared or scoped) which automagically delete their target objects.

Thomas Matthews
Everyone (including the original questioner) agrees that smart pointers are the way to go. Code evolves. There might not be more code after the delete when you first right it, but that's likely to change over time. Putting in the assignment helps when that happens (and costs almost nothing in the mean time).
Adrian McCarthy
+24  A: 

Setting pointers to NULL after you've deleted what it pointed to certainly can't hurt, but it's often a bit of a band-aid over a more fundamental problem: Why are you using a pointer in the first place? I can see two typical reasons:

  • You simply wanted something allocated on the heap. In which case wrapping it in a RAII object would have been much safer and cleaner. End the RAII object's scope when you no longer need the object. That's how std::vector works, and it solves the problem of accidentally leaving pointers to deallocated memory around. There are no pointers.
  • Or perhaps you wanted some complex shared ownership semantics. The pointer returned from new might not be the same as the one that delete is called on. Multiple objects may have used the object simultaneously in the meantime. In that case, a shared pointer or something similar would have been preferable.

My rule of thumb is that if you leave pointers around in user code, you're Doing It Wrong. The pointer shouldn't be there to point to garbage in the first place. Why isn't there an object taking responsibility for ensuring its validity? Why doesn't its scope end when the pointed-to object does?

jalf
So you're making the argument that there shouldn't have been a raw pointer in the first place, and anything involving said pointer shouldn't be blessed with the term "good practice"? Fair enough.
Mark Ransom
Well, more or less. I wouldn't say that *nothing* involving a raw pointer can be termed a good practice. Just that it's the exception rather than the rule. Usually, the presence of the pointer is an indicator that there's something wrong at a deeper level.
jalf
but to answer the immediate question, no, I don't see how setting pointers to null can ever *cause* errors.
jalf
Your first paragraph after the bulleted section forced me to upvote. Most of your pointer problems will disappear whether or not you null them if you toasterize your functionality.
San Jacinto
I disagree -- there are cases when a pointer is good to use. For example, there are 2 variables on the stack and you want to choose one of them. Or you want to pass an optional variable to a function.I would say, you should never use a raw pointer in conjunction with `new`.
rlbond
@rlbond: Fair point. I agree :)
jalf
Or `boost::optional`. :P Removes the need for a pointer.
GMan
True, but Boost isn't always an option, and sometimes the simplicity of a pointer may be preferable. (Everyone knows that a pointer may point to an object or null. People may not be familiar with boost::optional)
jalf
I completely disagree that setting them to NULL is a Band-Aid. It's actually a valid method of catching bugs. Using an RAII wrapper is certainly best whenever possible, but sometimes you have to implement your own wrapper, in which case, this is a valid question.
Adrian McCarthy
What do you mean by "your own wrapper"? Why shouldn't you use RAII for that wrapper? It is a band-aid in that it fixes errors that only occur because you didn't fix the *underlying* problem (that the variable is still in scope)
jalf
@jalf: Sometimes there isn't a pre-made wrapper class for the type of resource you need, in which case you have to write your own. The implementation of that will require a pointer (or other resource "handle").
Adrian McCarthy
@Adrian: Yes, but the implementation won't expose that pointer outside the scope in which it lives. If the pointer is a private member of the RAII wrapper you're writing, it will go out of scope the moment the wrapper does, and so there is nothing to set to NULL.
jalf
+9  A: 

Firstly, there are a lot of existing questions on this and closely related topics, for example http://stackoverflow.com/questions/704466/why-doesnt-delete-set-the-pointer-to-null.

In your code, the issue what goes on in (use p). For example, if somewhere you have code like this:

Foo * p2 = p;

then setting p to NULL accomplishes very little, as you still have the pointer p2 to worry about.

This is not to say that setting a pointer to NULL is always pointless. For example, if p were a member variable pointing to a resource who's lifetime was not exactly the same as the class containing p, then setting p to NULL could be a useful way of indicating the presence or absence of the resource.

anon
I agree that there are times when it won't help, but you seemed to imply that it could be actively harmful. Was that your intent, or did I read it wrong?
Mark Ransom
Whether there s a copy of the pointer is irrelevant to the question whether the pointer variable should be set to NULL. Setting it to NULL is a good practice on the same grounds cleaning the dishes after you're done with dinner is a good practice - while it's not a safeguard against all bugs a code can have, it does promote good code health.
Franci Penov
@Franci Lots of people seem to disagree with you. And whether there is a copy certainly is relevant if you try to use the copy after you deleted the original.
anon
@Mark I said it could hide errors, not cause them.
anon
Franci, there's a difference. You clean dishes because you use them again. You don't need the pointer after you delete it. It should be the last thing you do. *Better* practice is to avoid the situation altogether.
GMan
@Gman so I can't reuse a pointer variable? I allocate memory to that variable once and I never touch it after I delete that value?
Franci Penov
The assignment example is different that talking about whether the pointer should be `NULL`ed after it's deleted. Whether you set p to `NULL` depends on whether you're transferring ownership. This is orthogonal to the question at hand.
Adrian McCarthy
You can re-use a variable, but then it's no longer a case of defensive programming; it's how you designed the solution to the problem at hand. The OP is discussing whether this defensive style is something we should strive for, not of we'll ever set a pointer to null. And ideally, to your question, yes! Don't use pointers after you delete them!
GMan
@gma by that logig a pointer variable should be associated with the first value it is assigned and never be used for other values since it violates the principles of the defensive programming. But a pointer variable is no different than an int variables - it's just a memory storage for a value. if you want to be consistent you should not reuse other variables either and have them associated with the first value assigned to them. why are you feeling ok to set a counter to zero after you exit a loop, but you don't apply same to pointers?
Franci Penov
+1 for 'For example, if p were a member variable pointing to a resource who's lifetime was not exactly the same as the class containing p, then setting p to NULL could be a useful way of indicating the presence or absence of the resource.' which actually demonstrates an interesting point and accepts that pointers can be useful.
João Portela
@Neil if people always agreed with each other, there would never be a discussion, right? :-) but back to the topic - take the following code: `char a[10]; char b[5]; int i = 7; a[i] = 'x'; int j = i; i = 0; b[j] = 'z';`. Should we not set i to 0, because there's a copy of its value and using that copy can lead to a bug? how are i and j here different from a pointer? the semantic of their values is a memory address; the only difference is it's a relative memory address, not an absolute memory address. but that does not make it necessarily a valid memory address...
Franci Penov
A: 

Let me expand what you've already put into your question.

Here's what you've put into your question, in bullet-point form:


Setting pointers to NULL following delete is not universal good practice in C++. There are times when:

  • it is a good thing to do
  • and times when it is pointless and can hide errors.


However, there is no times when this is bad! You will not introduce more bugs by explicitly nulling it, you will not leak memory, you will not cause undefined behaviour to happen.

So, if in doubt, just null it.

Having said that, if you feel that you have to explicitly null some pointer, then to me this sounds like you haven't split up a method enough, and should look at the refactoring approach called "Extract method" to split up the method into separate parts.

Lasse V. Karlsen
I disagree with "there are no times when this is bad." Consider the amount of cutter this idiom introduces. You have a header being included in every unit that deletes something, and all those delete locations become *just slightly* less straight-forward.
GMan
There are times when it is bad. If someone tries to dereference your deleted-now-null pointer when they shouldn't, it probably won't crash and that bug is 'hidden.' If they dereference your deleted pointer which still has some random value in it, you are likely to notice and the bug will be easier to see.
Carson Myers
A: 

There is always Dangling Pointers to worry about.

GrayWizardx
Would it be so hard to put "dangling pointers" instead of "this"? :) At least it gives your answer some substance.
GMan
Fixed. Bad Habit. :(
GrayWizardx
<​​​​​​​​​​​​​​3
GMan
It's even the subject of a W3C QA tip, "Don't use 'click here' as link text": http://www.w3.org/QA/Tips/noClickHere
outis
+8  A: 

Setting a pointer to 0 (which is "null" in standard C++, the NULL define from C is somewhat different) avoids crashes on double deletes.

Consider the following:

Foo* foo = 0; // Sets the pointer to 0 (C++ NULL)
delete foo; // Won't do anything

Whereas:

Foo* foo = new Foo();
delete foo; // Deletes the object
delete foo; // Segfault

In other words, if you don't set deleted pointers to 0, you will get into trouble if you're doing double deletes. An argument against setting pointers to 0 after delete would be that setting pointers to 0 after deleting them just leaves double delete bugs undetected. It's up to you, really: Do you want your application to crash on a double free or not?

On a sidenote regarding managing object allocation, I suggest you take a look at std::auto_ptr or another smart pointer implementation, depending on your needs.

Håvard S
Your application won't always crash on a double delete. Depending on what happens between the two deletes, anything could happen. Most likely, you'll corrupt your heap, and you'll crash at some point later in a completely unrelated piece of code. While a segfault is usually better than silently ignoring the error, the segfault isn't guaranteed in this case, and it's of questionable utility.
Adam Rosenfield
The problem here is the fact that you have a double delete. Making the pointer NULL just hides that fact it does not correct it or make it any safer. Imagaine a mainainer comming back a year later and seeing foo deleted. He now believes he can re-use the pointer unfortunately he may miss the second delete (it may not even be in the same function) and now the re-use of the pointer now gets trashed by the second delete. Any access after the second delete is now a major problem.
Martin York
It's true the setting the pointer to `NULL` can mask a double delete bug. (Some might consider this mask to actually be a solution--it is, but not a very good one since it doesn't get to the root of the problem.) But not setting it to NULL masks the far (FAR!) more common problems of accessing the data after it has been deleted.
Adrian McCarthy
AFAIK, std::auto_ptr has been deprecated in the upcoming c++ standard
rafak
I wouldn't say deprecated, it makes it sound like the idea is just gone. Rather, it's being replaced with `unique_ptr`, which does what `auto_ptr` tried to do, with move semantics.
GMan
But then again, deprecated *is* the right term to use. :p
GMan
good point, "being replaced with" *is* the right thing to say ;-)
rafak
A: 

If you're going to reallocate the pointer before using it again (dereferencing it, passing it to a function, etc.), making the pointer NULL is just an extra operation. However, if you aren't sure whether it will be reallocated or not before it is used again, setting it to NULL is a good idea.

As many have said, it is of course much easier to just use smart pointers.

Edit: As Thomas Matthews said in this earlier answer, if a pointer is deleted in a destructor, there isn't any need to assign NULL to it since it won't be used again because the object is being destroyed already.

Dustin
A: 

I'll change your question slightly:

Would you use an uninitialized pointer? You know, one that you didn't set to NULL or allocate the memory it points to?

There are two scenarios where setting the pointer to NULL can be skipped:

  • the pointer variable goes out of scope immediately
  • you have overloaded the semantic of the pointer and are using its value not only as a memory pointer, but also as a key or raw value. this approach however suffers from other problems.

Meanwhile, arguing that setting the pointer to NULL might hide errors to me sounds like arguing that you shouldn't fix a bug because the fix might hide another bug. The only bugs that might show if the pointer is not set to NULL would be the ones that try to use the pointer. But setting it to NULL would actually cause exactly the same bug as would show if you use it with freed memory, wouldn't it?

Franci Penov
+1  A: 

Yes.

The only "harm" it can do is to introduce inefficiency (an unnecessary store operation) into your program - but this overhead will be insignificant in relation to the cost of allocating and freeing the block of memory in most cases.

If you don't do it, you will have some nasty pointer derefernce bugs one day.

I always use a macro for delete:

#define SAFEDELETE(ptr) { delete(ptr); ptr = NULL; }

(and similar for an array, free(), releasing handles)

You can also write "self delete" methods that take a reference to the calling code's pointer, so they force the calling code's pointer to NULL. For example, to delete a subtree of many objects:

static void TreeItem::DeleteSubtree(TreeItem *&rootObject)
{
    if (rootObject == NULL)
        return;

    rootObject->UnlinkFromParent();

    for (int i = 0; i < numChildren)
       DeleteSubtree(rootObject->child[i]);

    delete rootObject;
    rootObject = NULL;
}

edit

Yes, these techniques do violate some rules about use of macros (and yes, these days you could probably achieve the same result with templates) - but by using over many years I never ever accessed dead memory - one of the nastiest and most difficult and most time consuming to debug problems you can face. In practice over many years they have effectively eliminated a whjole class of bugs from every team I have introduced them on.

There are also many ways you could implement the above - I am just trying to illustrate the idea of forcing people to NULL a pointer if they delete an object, rather than providing a means for them to release the memory that does not NULL the caller's pointer.

Of course, the above example is just a step towards an auto-pointer. Which I didn't suggest because the OP was specifically asking about the case of not using an auto pointer.

Jason Williams
Macros are a bad idea, particularly when they look like normal functions. If you want to do this, use a templated function.
anon
Wow... I have never seen anything like `anObject->Delete(anObject)` invalidate the `anObject` pointer. That's just frightening. You should create a static method for this so that you are forced to do `TreeItem::Delete(anObject)` at the very least.
D.Shawley
Violates http://c2.com/cgi/wiki?PrincipleOfLeastAstonishment
Kevin Panko
Sorry, typed it in as a function rather than using the proper uppercase "this is a macro" form. Corrected. Also added a comment to explain myself better.
Jason Williams
And you're right, my quickly bashed out example was rubbish! Fixed :-). I was merely trying to think of a quick example to illustrate this idea: any code that deletes a pointer should ensure that the pointer is set to NULL, even if someone else (the caller) owns that pointer. So always pass in a reference to the pointer so it can be forced to NULL at the point of deletion.
Jason Williams
+2  A: 

As others have said, delete ptr; ptr = 0; is not going to cause demons to fly out of your nose. However, it does encourage the usage of ptr as a flag of sorts. The code becomes littered with delete and setting the pointer to NULL. The next step is to scatter if (arg == NULL) return; through your code to protect against the accidental usage of a NULL pointer. The problem occurs once the checks against NULL become your primary means of checking for the state of an object or program.

I'm sure that there is a code smell about using a pointer as a flag somewhere but I haven't found one.

D.Shawley
There's nothing wrong with using a pointer as a flag. If you're using a pointer, and `NULL` isn't a valid value, then you probably should be using a reference instead.
Adrian McCarthy
+1  A: 

If you have no other constraint that forces you to either set or not set the pointer to NULL after you delete it (one such constraint was mentioned by Neil Butterworth), then my personal preference is to leave it be.

For me, the question isn't "is this a good idea?" but "what behavior would I prevent or allow to succeed by doing this?" For example, if this allows other code to see that the pointer is no longer available, why is other code even attempting to look at freed pointers after they are freed? Usually, it's a bug.

It also does more work than necessary as well as hindering post-mortem debugging. The less you touch memory after you don't need it, the easier it is to figure out why something crashed. Many times I have relied on the fact that memory is in a similar state to when a particular bug occurred to diagnose and fix said bug.

MSN
A: 

My argument against it:

It hides real errors.
Error that could be exposed with profile tools will now not get caught. Now this is fine for the current version of the code. But later versions may re-expose the error and then generate a real problem.

A quick example:

Double delete:

int*  p    = createAPointer();

doGoodStuffWithP(p);

delete  p;
p = NULL;

.....

doBadStuffWithP(p);  // suppose there is a delete in here?

There is a bug in doBadStuffWithP() causing a second call to delete.
This is now not going to be exposed by testing becuase deleting NULL is not a problem.

This probably will not matter to much in version 1.
But modifications are done a year latter and p is now re-used adter the delete and the use of p continues after doBadStuffWithP() now you have a potential to use p after it has been deleted. Because we know that testing on version 2 is so much better than testing on version 1.

So I think it is a bas idea.
Admitadely I prefer to make sure scope kills p so I don't need to make it NULL.

But the hding of error scares me and I would prefer to find them in the first place.

Martin York
+4  A: 

I always set a pointer to NULL after deleting it.

  1. It can help catch many references to free'd memory (assuming your platform faults on a deref of NULL).

  2. It won't catch all references to free'd memory, for example, if you have a copy of the pointer lying around. But some is better than none.

  3. It will mask a double-delete, but I find those are far less common than accesses to already free'd memory.

  4. In many cases the compiler is going to optimize it away. So the argument that it's unnecessary doesn't persuade me.

  5. If you're already using RAII, the there aren't many deletes in your code to begin with, so the argument that the extra assignment causes clutter doesn't persuade me.

  6. It's often convenient, when debugging, to see the NULL value rather than a stale pointer.

  7. If this still bothers you, use a smart pointer or a reference instead.

I also set other types of resource handles to the no-resource value when the resource is free'd (which is typically only in the destructor of an RAII wrapper written to encapsulate the resource).

I worked on a large (9 million statements) commercial product (primarily in C). At one point, we used macro magic to NULL out the pointer when memory was freed. This immediately exposed lots of lurking bugs that were promptly fixed. As far as I can remember, we never had a double-free bug.

Adrian McCarthy
+1  A: 

Explicitly nulling after delete strongly suggests to a reader that the pointer represents something which is conceptually optional. If I saw that being done, I'd start worrying that everywhere in the source the pointer gets used that it should be first tested against NULL.

If that's what you actually mean, it's better to make that explicit in the source using something like boost::optional

optional<Foo*> p (new Foo);
// (use p.get(), but must test p for truth first!...)
delete p.get();
p = optional<Foo*>();

But if you really wanted people to know the pointer has "gone bad", I'll pitch in 100% agreement with those who say the best thing to do is make it go out of scope. Then you're using the compiler to prevent the possibility of bad dereferences at runtime.

That's the baby in all the C++ bathwater, shouldn't throw it out. :)

Hostile Fork
+6  A: 

I would almost set my deleted (or freed in my case) pointers to a garbage value:

char *ptr = malloc(...);
...
...
free(ptr);
ptr = 0xDEADBEEF;

This (probably) takes your pointer away from the heap, so it won't corrupt everything if you double-delete and don't crash. It will also fail (as it should) if someone tries to use it -- AND! It's easy to recognize when debugging, so you know when you see it: "Oh, I'm trying to dereference a pointer that I previously deleted".

That being said, this could be a really bad idea for some reason I haven't thought of, but it seems okay in my mind.

Carson Myers
+1. I like this, but you have to choose your trash value appropriately for your platform. Accesses through stale pointers is the most common error. Access through a NULL pointer will fault on virtually all VM systems. 0xDEADBEEF might not.
Adrian McCarthy
I see; like I said in the last part of my comment, I have no idea what all the consequences are -- I've never deployed something to a VM before and I'm not well versed in memory access (besides the right kind), so I suppose it's just an idea -- not a best-practice. at least not for some systems.
Carson Myers
A: 

I can imagine setting a pointer to NULL after deleting it being useful in rare cases where there is a legitimate scenario of reusing it in a single function (or object). Otherwise it makes no sense - a pointer needs to point to something meaningful as long as it exists - period.

Nemanja Trifunovic
A: 

If the code does not belong to the most performance-critical part of your application, keep it simple and use a shared_ptr:

shared_ptr<Foo> p(new Foo);
//No more need to call delete

It performs reference counting and is thread-safe. You can find it in the tr1 (std::tr1 namespace, #include < memory >) or if your compiler does not provide it, get it from boost.

beta4