是“delete p; p = NULL(nullptr);”反模式?

时间:2011-08-18 21:39:40

标签: c++

在SO上搜索某些内容时,我偶然发现了this question,其中一条评论得到了最多投票答案(对该投票最多的答案的第五条评论),表明delete p; p = NULL;反模式< / em>的。我必须承认我碰巧经常使用它,有时\大部分时间都用支票if (NULL != p)The Man他自己似乎在暗示它(请参阅destroy()函数示例),所以我真的很困惑为什么可能会被视为反模式这样可怕的事情。我使用它的原因如下:

  • 当我发布资源时,我也想让它无效以便进一步使用,而NULL是指示指针无效的正确工具
  • 我不想留下晃来晃去的指针
  • 我想避免双重\多个免费错误 - 删除NULL指针就像一个nop但删除一个悬空指针就像“射击自己在脚下”

请注意,我不是在“this”指针的上下文中提出这个问题,让我们假设我们没有生活在一个完美的C ++世界中,并且遗留代码确实存在并且它必须保持,所以请不要建议任何智能指针:)。

7 个答案:

答案 0 :(得分:15)

是的,我不建议这样做。

原因是额外设置为null只会在非常有限的上下文中有所帮助。如果你在析构函数中,指针本身在析构函数执行后就不会存在,这意味着它是否为null并不重要。

如果指针被复制,将p设置为null将不会设置其余的指针,然后你可以遇到与你期望发现删除的指针为空的额外问题相同的问题,你的指针变得非零并且对象不在那里也没有意义......

此外,它可能会隐藏其他错误,例如,如果您的应用程序试图多次删除指针,通过将指针设置为null,效果是第二次删除将转换为no-op,而while应用程序不会崩溃逻辑中的错误仍然存​​在(考虑到稍后编辑访问指针delete之前没有失败的指针... 如何失败? < / p>

答案 1 :(得分:6)

我建议这样做。

  • 显然,它在NULL是指针的有效值的上下文中是有效的。当然,这意味着如果它在其他地方使用,则必须进行检查。
  • 即使指针在技术上可能不是NULL,但在客户向您发送崩溃转储时,它确实有助于实际场景。如果它是NULL并且它不应该是(并且它没有被使用assert()进行测试(你应该这样做),那么很容易看出这就是问题 - 你会崩溃像mov eax, [edx + 4]或其他什么,你会看到edx是0,你知道问题是什么。另一方面,如果你没有指针NULL,但它被删除,那么可能会发生各种各样的事情。它可能会起作用,它可能会立即崩溃,它可能会在以后发生崩溃,它可能会显示出奇怪的东西 - 此时,任何发生的事情都是软不确定的。
  • 防御性节目是王道。这包括设置一个指向NULL的指针,即使你认为你不需要,并且在一些地方进行额外的NULL检查,即使你在技术上知道它不应该发生。
  • 即使你有两次指针经过删除的逻辑错误,最好不要崩溃并安全地处理它而不是崩溃。这可能意味着你做了额外的检查,你可能想要记录它,或者甚至可以优雅地结束程序,但这不仅仅是崩溃。客户不喜欢这样。

就个人而言,我使用一个内联函数,它将指针作为引用并将其设置为NULL。

答案 2 :(得分:4)

不,这不是反模式。

将指针设置为NULL是指示指针不再指向任何有效内容的完美方法。实际上,这正是NULL值的意思。

如果在这里要避免使用反模式,那么反模式将没有一套简单明确的规则/约定来规划程序如何管理其内存。这些规则是多么重要,只要它们能够避免泄漏和崩溃,并且只要你可以并且一直遵循它们。

答案 3 :(得分:4)

这取决于指针的使用方式。如果所有可以看到指针的代码都应该“知道”它不再有效 - 特别是在指针即将超出范围的析构函数中 - 不需要将它设置为null。

另一方面,指针可能表示有时存在的对象,有时不存在,并且您有像if (p) { p->doStuff(); }这样的代码在对象存在时作用于该对象。在这种情况下,显然,您应该在删除对象后将其设置为null。

后一种情况的重要区别在于指针变量的生命周期比它(有时)指向的对象的生命周期长得多,并且其无效性带有一些必须传达给其他对象的重要意义。程序的一部分。

答案 4 :(得分:3)

在我看来,反模式不是delete p; p = NULL;,而是assert(this != NULL)

我之所以使用反模式有两个原因 - 首先是为了增强坏代码在不隐藏的情况下崩溃的可能性,其次是在调试时使核心问题更加明显。我不会忘记我的代码assert只是因为它可能会抓住一些东西。

答案 5 :(得分:3)

虽然我认为@Mark Ransom正处于正确的轨道上,但我认为存在一个比assert(this!=NULL)更基本的问题。

我认为更有说服力的是你使用原始指针和new(直接)经常足以照顾。虽然这不是(必然)代码嗅觉/反模式/本身的任何东西,但它常常指向与C不必要的代码,并且没有利用标准库中的容器之类的东西。

即使标准容器不能很好地满足您的需求,您通常仍然可以将指针包装到一个足够小,足够简单的包中,这样的技术无关紧要 - 您限制了对相关指针的访问如此少量的代码,你可以浏览它,并确保它只有在它有效时才使用。正如Hoare所说,有两种做法:一种是保持代码如此简单,显然没有缺陷;另一个是使代码如此复杂,没有明显的缺陷。当你已经处理后一种情况时,这种技术似乎只对我有用。

最终,我认为这样做的愿望基本上是承认失败 - 而不是试图确保代码是正确的,你已经完成了相当于在沼泽旁边设置一个bug-zapper。它可以将小范围内的虫子的出现减少一小部分,但是如果对总人口有任何影响,那么就会有更多的自由繁殖,但它太小而无法衡量。

答案 6 :(得分:0)

  

原因是额外设置为null只会在非常有限的上下文中有所帮助。如果你在析构函数中,指针本身在析构函数执行后就不会存在,这意味着它是否为null并不重要。

必须更正此语句,因为它在C ++中为false。

当对象被破坏时,可以调用被破坏对象的其他函数(因为破坏过程需要它。)它通常被视为丑陋,但并不总是有好的解决方法。

因此,清除指针可能是避免问题的唯一好方法(即调用的其他函数可以在使用之前测试对象是否有效。)

然而,C ++中的一个好主意是使用智能对象(可能就是你所说的)。或多或少,一个类,它持有对象的引用,并确保在析构函数被击中时释放所述对象,而不是在一个对象中添加许多对象以同时销毁所有对象(虽然结果相同,但它更干净。)