空指针是否安全可删除?

348

删除空指针是否安全?

这是一种好的编码风格吗?


28
良好的实践是写C++程序时尽量避免使用delete命令。相反,应该使用RAII技术(资源获取即初始化)。例如,使用std::vector<T> v(100)代替T* p = new T[100],使用智能指针如unique_ptr<T>shared_ptr<T>等可以自动处理内存释放的指针,而不要使用原始指针等。 - fredoverflow
9
由于C++11中的make_shared和C++14中的make_unique,您的程序应该不包含任何newdelete - sp2danny
3
仍可能存在一些罕见情况需要使用new/delete,例如atomic<T*>:不允许使用atomic<unique_ptr<T>>,而atomic<shared_ptr<T>>的开销可能在某些情况下是无法接受的。 - atb
3
要使用RAII实现资源管理的类,您需要调用new和delete对吗?或者您是在说有一些模板类可以隐藏这一点。 - VinGarcia
3
重点是大多数用户/客户端(即非库)代码不应该写newdelete。当标准组件无法完成任务时,设计用于管理资源的类可以做必要的操作,但重点是它们对所管理的内存执行丑陋的操作,而不是最终用户代码。因此,创建自己的库/辅助类来执行new/delete操作,并使用该类代替它们。 - underscore_d
可能是在删除之前检查空指针是否有任何原因?的重复。 - underscore_d
9个回答

312

delete仍然会执行检查,因此在您的一侧进行检查会增加开销并且显得更加丑陋。一个非常好的实践是在delete之后将指针设置为NULL(有助于避免双重删除和其他类似的内存破坏问题)。

如果delete默认将参数设置为NULL,那就太好了。

#define my_delete(x) {delete x; x = NULL;}

(我知道R和L值是什么,但这样做不好吗?)


79
请注意,即使您在删除时将一个指针设置为 NULL,仍然可能有其他几个指针指向同一对象。 - sth
16
在我的代码中,大多数情况下,一旦指针被删除,它就会超出作用域。这比仅仅将其设置为NULL要安全得多。 - jalf
173
一种非常好的做法是删除后不要将指针设置为NULL。将指针设置为NULL会掩盖内存分配错误,这是非常糟糕的事情。正确的程序不会删除两次指针,而删除两次指针的程序应该会崩溃。 - Damon
17
@Alice:在这方面标准规定的内容是无关紧要的。30 年前,标准规定删除一个空指针是合法的,原因非常荒谬(很可能是 C 语言的遗留问题)。但是,重复删除同一指针(即使更改了其位模式)仍然是严重的程序错误,不是因为标准措辞的原因,而是由于程序逻辑和所有权所致。删除空指针也是一样的,因为空指针对应着“没有对象”,因此无法删除任何东西。一个程序必须准确地知道一个对象是否有效以及谁拥有它,以及何时可以删除它。 - Damon
35
@Damon,尽管你的严格所有权规则被废除,但非阻塞结构的稳健性可证明比基于锁的结构更强。我的同事们确实因为这些结构提供的增强执行概要和保持了严格线程安全性而爱我,这使得代码更易于推理(非常适用于维护)。然而,所有这些以及你所暗示的个人攻击都与正确性、有效性或所有权的任何定义无关。你提出的是一个好的经验法则,但它不是普适法则,也没有写进标准中。 - Alice
显示剩余28条评论

88

来自C++0x草案标准。

$5.3.5/2 - "[...]在任何一种情况下,delete运算符的操作数的值都可以是空指针值。[...]"

当然,没有人会对具有NULL值的指针执行'delete'操作,但这样做是安全的。理想情况下,应该避免编写删除NULL指针的代码。但是,在指针的删除(例如在容器中)发生循环时,有时候进行NULL指针值的删除是有用的。由于删除NULL指针值是安全的,因此可以真正编写不需要显式检查要删除的操作数是否为NULL的删除逻辑。

另外,C标准$7.20.3.2还指出,对NULL指针进行'free'操作不会执行任何操作。

free函数释放ptr指向的空间,即使其可以用于进一步的分配。如果ptr是空指针,则不执行任何操作。


2
如果这个答案没有在非优化代码中故意引入低效率,我会因为它的引用而非常喜欢它。正如被接受的答案所述,删除空指针是一个无操作。因此,在删除指针之前检查它是否为空完全是多余的。 - codetaku
在编程中,删除指向NULL的指针在至少一种情况下是完全合理的:当NULL表示资源从未被分配(但可能已经被分配)并且外部约束(例如需要让C API拥有该资源)防止使用RAII时。 - BCS

55

是的,它是安全的。

删除空指针是没有危害的,如果未分配的指针被初始化为零,然后仅仅被删除,在函数尾部往往会减少测试的次数。


由于前面的句子引起了混淆,这里提供一个例子,展示了描述内容的非异常安全写法:

void somefunc(void)
{
    SomeType *pst = 0;
    AnotherType *pat = 0;

    …
    pst = new SomeType;
    …
    if (…)
    {
        pat = new AnotherType[10];
        …
    }
    if (…)
    {
        …code using pat sometimes…
    }

    delete[] pat;
    delete pst;
}

这个示例代码可能有各种小问题,但我希望概念是清晰的。指针变量被初始化为零,这样在函数结尾处的delete操作不需要在源代码中测试它们是否非空;库代码会执行该检查。


我不得不多读几遍才能理解。你一定是指在方法的顶部或期间将它们初始化为零,而不是在尾部,是吗?否则,您只需删除清零和删除两个步骤。 - user207421
1
@EJP:一个非常合理的函数框架可能是这样的:void func(void) { X *x1 = 0; Y *y1 = 0; … x1 = new[10] X; … y1 = new[10] Y; … delete[] y1; delete[] x1; }。尽管代码中没有展示任何块结构或者跳转操作,但是由于在最开始就已经进行了初始化,因此在最后执行delete[]操作是安全的。如果在分配了x1并且在分配y1之前,有其他操作直接跳转到了程序末尾,并且没有对y1进行初始化,则会导致未定义行为。尽管代码可以在删除操作之前测试x1y1是否为空,但实际上这样做是没有必要的。 - Jonathan Leffler
知道它是安全的很好。但是,人们仍然可以使用“if(mypointer)”子句,因为它向阅读代码的人发出信号,表明所讨论的指针可能尚未设置,并且它表明这不是错误或笨拙的一部分,而是设计的一部分(我承认也可以使用注释)。 - Andrew Steane

22

删除空指针没有效果。虽然这不一定是良好的编码风格,因为它是不需要的,但也不算坏。

如果你正在寻找良好的编码实践,请考虑使用智能指针,这样你根本就不需要使用delete


22
人们想要删除空指针的时候,通常是因为他们不确定它是否为空...如果他们知道它是空的,那么他们就不会考虑删除并询问了 ;-). - Tony Delroy
@Tony:我的观点只是它不会产生任何影响,而存在这样的代码,删除一个有时包含NULL的指针并不一定是坏事。 - Brian R. Bondy
6
在性能、可读性和可维护性方面,IMO冗余的检查肯定是不好的。 - paulm
@paulm OP 明显不是在谈论那种糟糕的情况,更多的是 Seg Fault / UB 这种糟糕的情况。 - Winter

15

为了补充ruslik的答案,在C++14中你可以使用以下结构:

delete std::exchange(heapObject, nullptr);

4

只要您没有过载删除运算符,那么它就是安全的。但是如果您过载了删除运算符并且未处理null条件,则完全不安全。


1
你能为你的答案添加任何解释吗? - Marcin Nabiałek

0

关于这个问题,有一个常见问题解答可以回答。

C++语言保证如果p为空,则delete p不会执行任何操作。由于您可能会将测试反过来,并且由于大多数测试方法都要求您显式地测试每个分支点,因此您不应该放入冗余的if测试。



-4

我有经验表明,在编程中删除[] NULL(即数组语法)是不安全的(VS2010)。我不确定这是否符合C++标准。

删除NULL(标量语法)是安全的。


7
这是非法的,而且我不相信它。 - Konrad Rudolph
5
你应该能够删除任何空指针。所以如果程序出现错误,那么很可能是代码中存在Bug导致的。 - Mysticial
3
在第二种选择(删除数组)中,delete的操作数的值可以是空指针值或由先前的数组new表达式生成的指针值。 - sp2danny
1
@Opux 在回答中所述的VS2010的行为。正如其他评论所解释的那样,安全地delete[] NULL - Konrad Rudolph
2
这就是为什么我写“I don't believe it”而不是“that's wrong”。但我仍然不相信,而且这将是对标准的相当荒谬、愚蠢的违反。VC++实际上通常很好地遵循标准的限制,它违反它们的地方在历史上是有道理的。 - Konrad Rudolph
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接