52

我经常看到在删除指针之前检查遗留代码NULL,类似于,

if (NULL != pSomeObject) 
{
    delete pSomeObject;
    pSomeObject = NULL;
}

是否有任何理由NULL在删除指针之前检查它?之后设置指针的原因是什么NULL

4

10 回答 10

73

删除空指针是完全“安全”的;它实际上相当于无操作。

您可能希望在删除之前检查 null 的原因是尝试删除空指针可能表明您的程序中存在错误。

编辑

注意:如果你重载删除操作符,它可能不再是“安全的”delete NULL

于 2009-03-05T15:50:03.083 回答
40

C++ 标准保证在删除表达式中使用空指针是合法的(第 8.5.2.5/2 节)。但是,未指定这是否会调用释放函数(operator deleteoperator delete[];§8.5.2.5/7,注意)。

如果使用空指针调用默认释放函数(即由标准库提供),则调用无效(第 6.6.4.4.2/3 节)。

但是如果标准库没有提供释放函数会发生什么,即当我们重载operator delete(或operator delete[])时会发生什么,并没有明确说明。

一个称职的程序员会在释放函数中相应地处理空指针而不是在调用之前,如 OP 的代码所示。同样,在删除之后将指针设置为nullptr/NULL仅用于非常有限的目的。有些人喜欢本着防御性编程的精神这样做:在出现错误的情况下,它会使程序行为更加可预测:删除后访问指针将导致空指针访问,而不是对随机内存位置的访问。尽管这两个操作都是未定义的行为,但在实践中,空指针访问的行为更容易预测(它通常会导致直接崩溃而不是内存损坏)。由于内存损坏特别难以调试,因此重置已删除的指针有助于调试。

— 当然,这是治疗症状而不是治疗原因(即错误)。您应该将重置指针视为代码异味。干净、现代的 C++ 代码将明确内存所有权并进行静态检查(通过使用智能指针或等效机制),因此可以证明可以避免这种情况。

奖励:重载的解释operator delete

operator delete是(尽管它的名字)一个可以像任何其他函数一样被重载的函数。operator delete每次使用匹配的参数调用时,都会在内部调用此函数。对于operator new.

当您想要精确控制内存的分配方式时,重载operator new(然后也是operator delete)在某些情况下是有意义的。这样做甚至不是很困难,但必须采取一些预防措施以确保正确的行为。Scott Meyers 非常详细地描述了这一点Effective C++

现在,假设我们要重载全局版本的operator new调试。在我们这样做之前,关于以下代码中发生的事情的简短通知:

klass* pobj = new klass;
// … use pobj.
delete pobj;

这里实际发生了什么?那么上面可以大致翻译成下面的代码:

// 1st step: allocate memory
klass* pobj = static_cast<klass*>(operator new(sizeof(klass)));
// 2nd step: construct object in that memory, using placement new:
new (pobj) klass();

// … use pobj.

// 3rd step: call destructor on pobj:
pobj->~klass();
// 4th step: free memory
operator delete(pobj);

注意第 2 步,我们new用稍微奇怪的语法调用。这是对所谓放置new的调用,它接受一个地址并在该地址构造一个对象。该运算符也可以重载。在这种情况下,它只是用于调用类的构造函数klass

现在,事不宜迟,这里是运算符重载版本的代码:

void* operator new(size_t size) {
    // See Effective C++, Item 8 for an explanation.
    if (size == 0)
        size = 1;

    cerr << "Allocating " << size << " bytes of memory:";

    while (true) {
        void* ret = custom_malloc(size);

        if (ret != 0) {
            cerr << " @ " << ret << endl;
            return ret;
        }

        // Retrieve and call new handler, if available.
        new_handler handler = set_new_handler(0);
        set_new_handler(handler);

        if (handler == 0)
            throw bad_alloc();
        else
            (*handler)();
    }
}

void operator delete(void* p) {
    cerr << "Freeing pointer @ " << p << "." << endl;
    custom_free(p);
}

与大多数实现一样,此代码仅在内部使用malloc/的自定义free实现。它还创建一个调试输出。考虑以下代码:

int main() {
    int* pi = new int(42);
    cout << *pi << endl;
    delete pi;
}

它产生了以下输出:

Allocating 4 bytes of memory: @ 0x100160
42
Freeing pointer @ 0x100160.

现在,这段代码做了一些与标准实现完全不同的事情operator delete它没有测试空指针!编译器不会对此进行检查,因此上面的代码可以编译,但是当您尝试删除空指针时,它可能会在运行时产生令人讨厌的错误。

然而,正如我之前所说,这种行为实际上是出乎意料的,库编写者应该注意检查operator delete. 这个版本改进了很多:

void operator delete(void* p) {
    if (p == 0) return;
    cerr << "Freeing pointer @ " << p << "." << endl;
    free(p);
}

总之,虽然一个草率的实现operator delete可能需要在客户端代码中进行显式的空检查,但这是非标准行为,只能在遗留支持中被容忍(如果有的话)。

于 2009-03-05T15:59:28.677 回答
9

删除 null 是无操作的。在调用 delete 之前没有理由检查 null。

如果指针为 null 包含您关心的一些附加信息,您可能需要检查 null 是否有其他原因。

于 2009-03-05T15:50:15.593 回答
9

删除内部检查 NULL。你的测试是多余的

于 2009-03-05T15:59:31.873 回答
4

根据 C++03 5.3.5/2,删除空指针是安全的。以下内容来自标准:

在任一替代方案中,如果 delete 的操作数的值是空指针,则该操作无效。

于 2012-04-21T14:20:26.203 回答
3

如果 pSomeObject 为 NULL,则 delete 不会做任何事情。所以不,您不必检查 NULL。

如果某些笨蛋有可能尝试使用指针,我们认为在删除指针后将其分配为 NULL 是一种很好的做法。使用 NULL 指针比使用指向谁知道什么的指针稍微好一些(NULL 指针会导致崩溃,指向已删除内存的指针可能不会)

于 2009-03-05T15:51:22.830 回答
1

没有理由在删除之前检查 NULL。如果在代码中的某处通过执行 NULL 检查是否已经分配了某个对象,则可能需要在删除后分配 NULL。一个示例是按需分配的某种缓存数据。每当您清除缓存对象时,您将 NULL 分配给指针,以便分配对象的代码知道它需要执行分配。

于 2009-03-05T15:58:52.500 回答
0

我相信以前的开发人员对其进行了“冗余”编码以节省一些毫秒:在删除对象时将指针设置为 NULL 是一件好事,因此您可以在删除对象后立即使用如下行:

if(pSomeObject1!=NULL) pSomeObject1=NULL;

但是无论如何 delete 都会进行精确的比较(如果它为 NULL,则什么也不做)。为什么要这样做两次?在调用 delete 之后,您始终可以将 pSomeObject 分配给 NULL,而不管它的当前值是什么 - 但如果它已经具有该值,这将有点多余。

所以我敢打赌,这些行的作者试图确保 pSomeObject1 在被删除后始终为 NULL,而不会产生可能不必要的测试和分配的成本。

于 2009-03-05T20:46:17.347 回答
-2

这取决于你在做什么。例如,一些较旧的实现如果传递了一个指针free,就不会高兴。NULL一些图书馆仍然有这个问题。例如,XFree在 Xlib 库中说:

描述

XFree 函数是释放指定数据的通用 Xlib 例程。您必须使用它来释放由 Xlib 分配的任何对象,除非为该对象明确指定了备用函数。不能将 NULL 指针传递给此函数。

所以考虑释放NULL指针作为一个错误,你会很安全。

于 2009-03-05T16:04:03.627 回答
-5

至于我的观察,使用 delete 删除空指针在基于 unix 的机器(如 PARISC 和 itanium)中是安全的。但是对于 Linux 系统来说是非常不安全的,因为这个过程会崩溃。

于 2009-08-07T01:21:06.747 回答