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

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

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

有帮助吗?

解决方案

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

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

其他提示

C ++标准保证在 delete-expression (§ 8.5.2.5/2)中使用空指针是合法的。但是,未指定是否会调用释放函数( operator delete operator delete [] ;§ 8.5.2.5/ 7,注意)。

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

但如果标准库没有提供解除分配功能,那么未指定会发生什么?— 当我们重载 operator delete (或 operator delete [] )时会发生什么。

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

&#8212;当然,这是治疗症状而不是病因(即病毒)。 您应该将重置指针视为代码气味。干净,现代的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 。这是对所谓的 placement 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 的标准实现:它没有测试空指针!编译器不检查这个上面的代码编译

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

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

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

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

  

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

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

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

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

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

if(pSomeObject1!=NULL) pSomeObject1=NULL;

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

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

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

  

说明

     

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

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

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

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