我首先要说的是, 使用智能指针,您将永远不必担心这个问题。

下面的代码有什么问题?

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

这是由 答案和评论 另一个问题。一条评论来自 尼尔·巴特沃斯 产生了一些赞成票:

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

在很多情况下它没有帮助。但根据我的经验,这不会造成伤害。有人启发我一下。

有帮助吗?

解决方案

设置一个指针为0(这是在标准C“空” ++中,NULL选自C定义有些不同)避免了双重删除崩溃。

考虑如下:

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

鉴于:

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

在换句话说,如果您没有设置删除指针为0,你会惹上麻烦,如果你正在做的双删除。针对指针设置为后删除参数0是,这样做只是掩盖双重删除错误,并使得他们无法处理。

这是最好的没有双重删除错误,很明显,但根据所有权语义和对象的生命周期,这可能很难在实践中实现。我喜欢掩蔽双重删除错误超过UB。

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

其他提示

设定的指针NULL后你删除的是什么指出,肯定不能伤害,但是往往一点一带的援助超过一个更为根本的问题:为什么你使用一个指在首位?我可以看到的两个典型的原因:

  • 你只是想要的东西上分配堆。在这种情况下包装在一个RAII对象将是更安全和更清洁。结束RAII对象的范围时,你不再需要的对象。那是怎么 std::vector 的工作,它解决了的问题不小心留下指向释放的记忆有没有指针。
  • 或许你想一些复杂的共同拥有语义。指针返回 new 可能不是同一个 delete 被称为。多个对象可以使用的对象同时在此期间。在这种情况下,一个共用的指针或类似的东西已经取的。

我的经验法则是,如果你留下的指针在用户代码,你这样做是错误的。指针不应该在那里的一点垃圾放在第一位。为什么没有一个对象负责确保其有效期?为什么不是其范围结束时对对象?

我有一个更好的最佳实践:如果可能的话,结束变量的作用域

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

我总是设置一个指向 NULL (现在 nullptr)之后,删除的目的(s)分。

  1. 它可以帮助赶上多次提到存释放(假定平台上的故障一个deref的空指针)。

  2. 它不会抓住所有提到免费的会存储器,例如,如果你有副本的指针。但是,一些比没有好。

  3. 它会掩盖一个双删除,但我找到那些远较不常见于访问已被释放的记忆。

  4. 在许多情况下编译器是要优化它。这样的论点,即这是不必要的不能说服我。

  5. 如果你已经在使用RAII,则没有很多 delete在你的代码开始,因此该论点,即额外的分配会导致混乱不会说服我。

  6. 它的经常方便,在调试,看到空值,而不是一种陈旧的指针。

  7. 如果这仍然困扰你的,使用一个聪明的指针或一个参考,而不是。

我还设置了其他类型的资源处理给没有资源价值的资源是免费的会(这是通常只有在该析构的RAII包写封的资源)。

我曾在大(9万发言)商业产品(主要是C)。在一点上,我们使用的宏魔法空出的指针时,记忆被释放。这立即暴露出许多潜在的错误已及时固定的。尽我所能记得,我们从来没有一个双免费的错误。

更新: Microsoft认为,这是一个好的做法,为安全和建议的实践在它们的SDL政策。显然MSVC++11 踩删除的指针 自动(在许多情况下)如果编译/SDL的选择。

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

在您的代码中,问题是(使用 p)中发生了什么。例如,如果某处有这样的代码:

Foo * p2 = p;

然后将 p 设置为 NULL 效果甚微,因为您仍然需要担心指针 p2 。

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

如果存在delete后更多的代码,是。当指针在构造或在方法或功能的结束时被删除,号

此比喻的要点是要提醒程序员,在运行时,该对象已被删除。

一个更好的做法是使用智能指针(共享或作用域),其自动地删除他们的目标对象。

正如其他人所说,delete ptr; ptr = 0;不会造成恶魔飞出你的鼻子。但是,它鼓励ptr的使用作为排序的标志。该代码变得与delete和设置指针NULL散落。下一步是通过您的代码散落if (arg == NULL) return;保护免受NULL指针意外使用。出现该问题的一次针对NULL支票成为你的检查对象或程序的状态的主要手段。

我敢肯定,有关于使用指针作为一个标志某处代码味道,但我还没有找到一个。

我会改变你的问题略:

你会用一种初始化 指针?你知道吗,一个你没有 设置为空或分配的记忆 点到?

在两种情况下设定的指针指向空可以跳过:

  • 指针的变量超出范围,立即
  • 你有超载的义的指针,使用的是其价值,不仅作为一个存储指针,但也作为一个关键或原始价值。这种做法而遭受的其他问题。

与此同时,认为设定的指针指向空可能隐藏的错误,给我听起来像认为你不应该解决的一个错误,因为修复可能隐藏的另一个错误。唯一的错误,这可能显示如果指针不是设置NULL将那些试图使用的指针。但是,其设置为空会实际上导致完全相同的错误,将显示如果使用它释放的内存,不是吗?

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

对我来说,问题不是“这是个好主意吗?”但是,“我会阻止或允许这样做的什么行为?”例如,如果这允许其他代码看到指针不再可用,为什么其他代码甚至在释放后都试图查看释放指针?通常,这是一个错误。

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

显式置零后强烈暗示删除到该指针代表一些东西,在概念上是读取器的可选的的。如果我看到了正在做,我开始担心无处不在的源指针被使用,它应该对NULL进行首次测试。

如果这就是你真正的意思是,这是更好地使在使用类似的升压::可选

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

但如果你真的想让人们知道的指针已经“坏”,我会100%的协议场上与那些谁要说到做到的最好的事情就是让它走出去的范围。然后,您使用的编译器,以防止在运行时坏非关联的可能性。

这是在所有的C ++洗澡水宝宝,不应该把它扔出去。 :)

在具有适当的错误检查结构良好的程序,没有理由的为其指定空。 0独自站在作为这方面的一个普遍公认的无效值。失败硬很快失效。

很多的反对分配0的观点表明,它的可能的隐藏的错误或复杂的控制流。从根本上说,即或者是一个错误的上游(未你的错(抱歉坏双关语))或代表程序员的另一个错误 - 甚至也许是指示程序流长得太复杂

如果程序员想引进的使用可以为null特殊值,写身边所有必要躲避的指针,这是他们特意推出的并发症。该隔离越好,越早发现误用的情况下,和少他们能够蔓延到其他程序。

结构良好的程序可以使用C ++特性,以避免这些情况下进行设计。您可以使用引用,或者你可以说:“通过/使用空或无效的论点是错误的” - 的做法这同样适用于容器,如智能指针。提高一致性和正确的行为,从越来越远禁止这些错误。

从那里,你只具有非常有限的范围和背景,其中一个空指针可能存在(或允许)。

同样可以应用到未const指针。继指针的值是微不足道的,因为它的范围是如此之小,而且使用不当检查,并明确界定。如果你的工具集和工程师不能遵循以下程序快速阅读或者有不合适的错误检查或不一致/宽大程序流程,您有其他的,更大的问题。

最后,你的编译器和环境可能具有时代一些卫兵,当你想介绍的错误(涂鸦),检测访问释放的内存,并赶上其他相关UB。您也可以提出类似的诊断为你的程序,往往在不影响现有的方案。

让我展开什么你已经把你的问题。

这里是什么你已经把你的问题,要点的形式:


设定的指针NULL以下删除不是普遍的良好做法C++。有些时候:

  • 这是一个好的事情要做
  • 和时间时,这是毫无意义的和可以隐藏的错误。

然而,有的是 没有时间 当这是 糟糕!你会 介绍更多的错误,明确消除了它,你不会 泄漏 存储器,你不会 导致不明确的行为 发生。

所以,如果有疑问,只是空。

有的说,如果你觉得你必须明确null一些指针,然后对我来说这听起来像是你没有分开的方法有足够和应该看看的重构办法称为"提取方法"分裂立法进入分开的部分。

唯一的“伤害”它能做的就是引入低效(不必要的存储操作)到你的程序 - 但这种开销将是关系到分配和释放在大多数情况下,内存块的成本微不足道

如果你不这样做,你的将会的有一些讨厌的指针derefernce错误的一天。

我总是用一个宏删除:

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

(以及类似的用于阵列,免费(),释放手柄)

您也可以写“自我删除”即采取调用代码的指针的引用方法,因此他们迫使调用代码的指针为NULL。例如,要删除的许多对象的子树:

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;
}

修改

是,这些技术违反了有关使用宏的一些规则(是的,这几天你也许可以实现与模板相同的结果) - 但通过多年来我从来没有访问死内存 - 的耗时你可以面对调试问题最讨厌和最困难和最花时间的。在实践中多年来他们已经有效地消除一个whjole类虫子从我已经介绍了他们对每一支球队。

也有很多方法,你可以实现以上 - 我只是想说明迫使人们为NULL指针,如果他们删除对象,而不是提供一种手段为他们释放不空的记忆的观念呼叫者的指针。

当然,上面的例子仅仅是一个朝向自动指针步骤。我没有建议,因为OP是专门询问不使用自动指针的情况。

“有次当它是做一件好事,并且时候它是没有意义的,可以隐藏错误”

我可以看到两个问题: 这一简单的代码:

delete myObj;
myobj = 0

变在多线程环境中一个换衬:

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

唐诺伊费尔德的“最佳实践”并不总是适用。例如。在一个汽车项目我们必须设置指针为0,即使在析构函数。我可以在安全关键软件想象这样的规则并不少见。这是比较容易(和明智的)跟着他们不是试图说服 球队/代码检查工具用于在代码中的每个指针的使用,即一个线归零这个指针是多余的。

另一个危险是依靠该技术在例外的使用代码:

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

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

在这样的代码要么你产生资源泄漏和推迟问题或进程崩溃。

所以,这两个问题,通过我的头去自发地(香草萨特将肯定告诉更多)让我的那种所有的问题“怎样避免使用智能指针,并与正常指针安全地完成工作。”为已过时

有总是悬摆指针的担心。

如果您要在再次使用指针之前重新分配该指针(取消引用它、将其传递给函数等),则将指针设置为 NULL 只是一个额外的操作。但是,如果您不确定在再次使用它之前是否会重新分配它,那么将其设置为 NULL 是一个好主意。

正如许多人所说,使用智能指针当然要容易得多。

编辑:正如托马斯·马修斯在《 这个之前的答案, ,如果在析构函数中删除指针,则无需为其分配 NULL,因为它不会再次使用,因为该对象已被销毁。

我可以想像删除它是在极少数情况下有用后的指针设置为NULL,其中有一个合法在一个单一的功能(或对象)重新使用的场景。否则,它是没有意义的 - 的指针需要,只要它存在以指向一些有意义 - 周期

如果代码不属于你的应用程序的性能最关键的部分,保持简单和使用一个shared_ptr:

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

它执行引用计数和是线程安全。您可以在TR1找到它(的std :: TR1的命名空间,的#include <记忆>),或者如果你的编译器不提供的,由升压得到它。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top