C++ 删除指针后将指针设为 NULL 是一种好习惯吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1931126/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:37:36  来源:igfitidea点击:

Is it good practice to NULL a pointer after deleting it?

c++pointersnulldynamic-allocation

提问by Mark Ransom

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 commentsto another question. One comment from Neil Butterworthgenerated 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.

在删除之后将指针设置为 NULL 在 C++ 中不是普遍的好习惯。有时这样做是件好事,有时却毫无意义并且可以隐藏错误。

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

在很多情况下它都无济于事。但根据我的经验,它不会受到伤害。有人开导我。

采纳答案by H?vard S

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

将指针设置为 0(在标准 C++ 中为“空”,C 中的 NULL 定义有些不同)可避免双重删除时崩溃。

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; // Undefined behavior 

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 doing so just masks double delete bugs and leaves them unhandled.

换句话说,如果您不将已删除的指针设置为 0,那么在进行双重删除时就会遇到麻烦。反对在删除后将指针设置为 0 的一个论点是,这样做只会掩盖双重删除错误并使它们无法处理。

It's best to not have double delete bugs, obviously, but depending on ownership semantics and object lifecycles, this can be hard to achieve in practice. I prefer a masked double delete bug over UB.

显然,最好不要有双重删除错误,但是根据所有权语义和对象生命周期,这在实践中很难实现。与 UB 相比,我更喜欢隐藏的双重删除错误。

Finally, a sidenote regarding managing object allocation, I suggest you take a look at std::unique_ptrfor strict/singular ownership, std::shared_ptrfor shared ownership, or another smart pointer implementation, depending on your needs.

最后,关于管理对象分配的旁注,我建议您根据需要查看std::unique_ptr严格/单一所有权、std::shared_ptr共享所有权或其他智能指针实现。

回答by jalf

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:

在删除它所指向的内容后将指针设置为 NULL 肯定不会有什么坏处,但它通常是一个更基本问题的创可贴:首先为什么要使用指针?我可以看到两个典型的原因:

  • 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::vectorworks, 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 newmight not be the same as the one that deleteis 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.
  • 你只是想在堆上分配一些东西。在这种情况下,将它包装在 RAII 对象中会更安全、更干净。当您不再需要该对象时,结束 RAII 对象的范围。这就是std::vector工作原理,它解决了不小心留下指向已释放内存的指针的问题。没有指针。
  • 或者您可能想要一些复杂的共享所有权语义。返回的指针new可能与delete调用的指针不同。多个对象可能同时使用了该对象。在这种情况下,共享指针或类似的东西会更可取。

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?

我的经验法则是,如果你在用户代码中留下指针,你就做错了。指针不应该首先指向垃圾。为什么没有对象负责确保其有效性?当指向的对象结束时,为什么它的范围没有结束?

回答by Don Neufeld

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

我有一个更好的最佳实践:在可能的情况下,结束变量的作用域!

{
    Foo* pFoo = new Foo;
    // use pFoo
    delete pFoo;
}

回答by Adrian McCarthy

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

在删除它指向的对象后,我总是设置一个指向NULL(现在nullptr)的指针。

  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.

  1. 它可以帮助捕获对已释放内存的许多引用(假设您的平台在空指针的 deref 上出现错误)。

  2. 例如,如果您有指针的副本,则它不会捕获对已释放内存的所有引用。但有总比没有好。

  3. 它会掩盖双重删除,但我发现那些比访问已经释放的内存要少得多。

  4. 在许多情况下,编译器会对其进行优化。所以没有必要的论点并不能说服我。

  5. 如果您已经在使用 RAII,那么delete开始时您的代码中的 s并不多,因此额外赋值导致混乱的论点并不能说服我。

  6. 在调试时,查看空值而不是陈旧的指针通常很方便。

  7. 如果这仍然困扰您,请改用智能指针或引用。

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

当资源被释放时,我还将其他类型的资源句柄设置为 no-resource 值(通常仅在为封装资源而编写的 RAII 包装器的析构函数中)。

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.

我开发了一个大型(900 万条语句)商业产品(主要是 C 语言)。有一次,我们使用宏魔法在释放内存时将指针清零。这立即暴露了许多迅速修复的潜在错误。据我所知,我们从未遇到过双重释放错误。

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 pointerautomatically (in many circumstances) if you compile with the /SDL option.

更新:Microsoft 认为这是一种很好的安全做法,并在其 SDL 策略中推荐了这种做法。显然,如果您使用 /SDL 选项进行编译,MSVC++11 将自动(在许多情况下)踩踏已删除的指针

回答by Adrian McCarthy

Firstly, there are a lot of existing questions on this and closely related topics, for example Why doesn't delete set the pointer to NULL?.

首先,关于这个和密切相关的主题有很多现有的问题,例如为什么不删除将指针设置为 NULL?.

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

在您的代码中,问题发生在(使用 p)中。例如,如果某个地方有这样的代码:

Foo * p2 = p;

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

然后将 p 设置为 NULL 的作用很小,因为您仍然需要担心指针 p2。

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.

这并不是说将指针设置为 NULL 总是毫无意义的。例如,如果 p 是指向资源的成员变量,该资源的生命周期与包含 p 的类不完全相同,那么将 p 设置为 NULL 可能是指示资源存在或不存在的有用方法。

回答by Thomas Matthews

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.

如果 之后有更多代码delete,是的。当在构造函数中或在方法或函数结束时删除指针时,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.

更好的做法是使用智能指针(共享或范围),它会自动删除其目标对象。

回答by D.Shawley

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 ptras a flag of sorts. The code becomes littered with deleteand 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 NULLpointer. The problem occurs once the checks against NULLbecome your primary means of checking for the state of an object or program.

正如其他人所说,delete ptr; ptr = 0;不会让恶魔从你的鼻子里飞出来。但是,它确实鼓励将ptr用作某种标志。代码变得杂乱无章,delete并将指针设置为NULL. 下一步是分散if (arg == NULL) return;您的代码以防止意外使用NULL指针。一旦检查NULL成为您检查对象或程序状态的主要手段,就会出现问题。

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

我确信在某处使用指针作为标志有一种代码味道,但我还没有找到。

回答by MSN

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.

如果您没有其他约束强制您在删除指针后设置或不将指针设置为 NULL(Neil Butterworth提到了一个这样的约束),那么我个人的偏好是保留它。

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.

它还做了更多不必要的工作,并阻碍了事后调试。在你不需要内存后,你接触它的次数越少,就越容易找出崩溃的原因。很多时候,我依赖于内存处于与特定错误发生时相似的状态来诊断和修复所述错误的事实。

回答by Franci Penov

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?

你会使用一个未初始化的指针吗?您知道,您没有将其设置为 NULL 或分配它指向的内存吗?

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

有两种情况可以跳过将指针设置为 NULL:

  • 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?

同时,认为将指针设置为 NULL 可能会隐藏错误对我来说听起来像是在争论您不应该修复错误,因为修复可能会隐藏另一个错误。如果指针未设置为 NULL,可能会显示的唯一错误是尝试使用指针的错误。但是将它设置为 NULL 实际上会导致与释放内存一起使用时所显示的完全相同的错误,不是吗?

回答by HostileFork says dont trust SE

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.

删除后显式归零向读者强烈建议该指针表示概念上可选的内容。如果我看到这样做了,我会开始担心源代码中的任何地方都会使用指针,因此应该首先针对 NULL 对其进行测试。

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

如果这就是您的实际意思,最好使用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.

但是,如果你真的想让人们知道指针“变坏”了,我会 100% 同意那些说最好的做法是让它超出范围的人。然后您使用编译器来防止在运行时出现错误取消引用的可能性。

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

那是所有 C++ 洗澡水中的宝贝,不应该把它扔掉。:)