在删除之前有没有理由检查NULL指针?

时间:2009-03-05 15:47:01

标签: c++ pointers null delete-operator

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

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

在删除之前有没有理由检查NULL指针?之后将指针设置为NULL的原因是什么?

10 个答案:

答案 0 :(得分:67)

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

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

答案 1 :(得分:35)

C ++标准保证在 delete-expression (§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 / {{1删除后仅用于非常有限的目的。有些人喜欢本着defensive programming的精神这样做:它会使程序行为在出现错误时更容易预测:删除后访问指针会导致空指针访问而不是访问随机记忆位置。尽管两个操作都是未定义的行为,但是在实践中空指针访问的行为更加可预测(它通常导致直接崩溃而不是内存损坏)。由于内存损坏特别难以调试,因此重置已删除的指针有助于调试。

- 当然这是治疗症状而不是病因(即病毒)。 您应该将重置指针视为代码气味。干净,现代的C ++代码将使内存所有权清晰且静态检查(通过使用智能指针或等效机制),从而可以证明避免这种情况。

奖励:对重载NULL

的解释

operator delete是(尽管它的名字)一个可能像任何其他函数一样重载的函数。对于每个具有匹配参数的operator delete调用,都会在内部调用此函数。 operator delete也是如此。

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

现在,我们只想说我们要重载operator delete的全局版本以进行调试。在我们这样做之前,请简要注意以下代码中发生的事情:

operator new

这里究竟发生了什么?以上可以粗略地翻译成以下代码:

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

注意步骤2,我们用稍微奇怪的语法调用// 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); 。这是对所谓的 placement 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); } 的草率实现可能需要在客户端代码中进行显式空检查,但这是非标准行为,只应在遗留支持中容忍(,如果有的话)。

答案 2 :(得分:9)

在内部删除对NULL的检查。您的测试是多余的

答案 3 :(得分:7)

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

如果指针为null,则可能需要检查null是否存在其他原因。

答案 4 :(得分:4)

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

  

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

答案 5 :(得分:3)

如果pSomeObject为NULL,则delete不会执行任何操作。所以不,你不必检查NULL。

我们认为在删除指针后为指针指定NULL是一个好习惯,如果一些指关节可以尝试使用指针的话。使用NULL指针稍微好于使用指向谁知道什么的指针(NULL指针将导致崩溃,指向已删除内存的指针可能不会)

答案 6 :(得分:1)

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

答案 7 :(得分:0)

我相信之前的开发人员将其编写为“冗余”以节省几毫秒: 将指针删除后设置为NULL是一件好事,因此在删除对象后可以使用如下所示的行:

if(pSomeObject1!=NULL) pSomeObject1=NULL;

但是然后删除正在进行那种精确的比较(如果它为NULL则不执行任何操作)。为什么这两次?在调用delete之后,无论其当前值如何,您总是可以将pSomeObject分配给NULL - 但如果它已经具有该值,那么这将略微多余。

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

答案 8 :(得分:-2)

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

  

说明

     

XFree功能是一个   通用的Xlib例程   释放指定的数据。你必须   用它来释放任何对象   由Xlib分配,除非是备用   明确指定了函数   物体。 NULL指针不能   传递给了这个函数。

因此,考虑将NULL指针释放为错误,您将是安全的。

答案 9 :(得分:-5)

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