Is it good practice to NULL a pointer after deleti

2018-12-31 19:11发布

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.

18条回答
浮光初槿花落
2楼-- · 2018-12-31 19:28

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.

查看更多
像晚风撩人
3楼-- · 2018-12-31 19:30

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.

查看更多
看风景的人
4楼-- · 2018-12-31 19:30

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

查看更多
浪荡孟婆
5楼-- · 2018-12-31 19:33

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.

查看更多
其实,你不懂
6楼-- · 2018-12-31 19:36

"There are times when it is a good thing to do, and times when it is pointless and can hide errors"

I can see two problems: That simple code:

delete myObj;
myobj = 0

becomes to a for-liner in multithreaded environment:

lock(myObjMutex); 
delete myObj;
myobj = 0
unlock(myObjMutex);

The "best practice" of Don Neufeld don't apply always. E.g. in one automotive project we had to set pointers to 0 even in destructors. I can imagine in safety-critical software such rules are not uncommon. It is easier (and wise) to follow them than trying to persuade the team/code-checker for each pointer use in code, that a line nulling this pointer is redundant.

Another danger is relying on this technique in exceptions-using code:

try{  
   delete myObj; //exception in destructor
   myObj=0
}
catch
{
   //myObj=0; <- possibly resource-leak
}

if (myObj)
  // use myObj <--undefined behaviour

In such code either you produce resource-leak and postpone the problem or the process crashes.

So, this two problems going spontaneously through my head (Herb Sutter would for sure tell more) make for me all the questions of the kind "How to avoid using smart-pointers and do the job safely with normal pointers" as obsolete.

查看更多
长期被迫恋爱
7楼-- · 2018-12-31 19:38

I always set a pointer to NULL (now nullptr) after deleting the object(s) it points to.

  1. It can help catch many references to freed memory (assuming your platform faults on a deref of a null pointer).

  2. It won't catch all references to free'd memory if, for example, you have copies 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 freed 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, then 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.

Update: Microsoft believes that it's a good practice for security and recommends the practice in their SDL policies. Apparently MSVC++11 will stomp the deleted pointer automatically (in many circumstances) if you compile with the /SDL option.

查看更多
登录 后发表回答