当我在空指针上调用“delete”时,为什么会调用“operator delete”?

17
在阅读这个问题的答案时,我注意到答案(例如这个)暗示即使在空指针上执行delete语句,也可以调用operator delete
因此,我编写了以下简短代码片段:
class Test {
public:
    void* operator new( size_t ) { /*doesn't matter*/ return 0; }
    void operator delete( void* ptr ) {
        ptr; //to suppress warning and have a line to put breakpoint on
    }
};

int main()
{
    Test* ptr = 0;
    delete ptr;
}

令我惊讶的是,使用空指针ptr时会调用Test::operator delete()

据我所知,operator new用于分配内存,operator delete将内存返回给分配器。如果我在空指针上调用delete语句,这意味着指针后面没有对象,并且没有内存可以返回给分配器。

delete语句包括调用析构函数。当我传递一个空指针时,析构函数肯定不会被调用 - C ++会处理这个问题。那么为什么在这种情况下调用operator delete呢?


请问为什么在分配零长度数组时会调用 operator new 函数:new Test[0]; ... ;) - Yakov Galka
1
@ybungalobill:这很容易 - 标准要求返回的指针必须有效且不同。 - sharptooth
请注意,如果您将析构函数声明为虚函数,那么重载的 operator delete 将不会被调用。实现通常会直接从析构函数中调用该函数,并在调用析构函数时不进行空指针检查(从而节省一些指令)。只有在需要进行虚拟分派时才会提前执行检查。 - avakar
还有一件事:从析构函数中调用运算符很方便,因为它可以节省一个虚拟分派。 - avakar
2个回答

19
即将推出的C++0x标准中(第5.3.5节[expr.delete]),语言如下:
如果delete表达式的操作数值不是空指针值,则delete表达式将调用一个释放函数(3.7.4.2)。否则,未指定是否调用释放函数。[注意:无论对象或数组的某个元素的析构函数是否抛出异常,都会调用释放函数。—注]。
因此,这是未指定的行为,一些编译器在删除空指针时可能会调用operator delete,而其他编译器则可能不会。
编辑:标准中使用的“释放函数”这一术语似乎引起了一些混淆。它带有一个参考文献。3.7.4.2 [basic.stc.dynamic.deallocation]中的一些关键语言可能有助于澄清:
如果类T具有名为operator delete的成员释放函数,该函数恰好有一个参数,则该函数是常规(非放置)释放函数。
标准还非常明确地要求用户定义的operator delete需要接受一个空指针值作为参数:
提供给释放函数的第一个参数的值可以是空指针值;如果是这样,并且释放函数是标准库中提供的,则调用没有效果。
但由于未指定的行为5.3.5,您不应该依赖于指针为空时调用operator delete

嗯,这段代码片段讨论的是释放函数,而不是运算符。 - Hans Passant
C++03似乎在这个问题上表述得含糊不清。它说“如果delete的操作数值是空指针,则操作没有效果”,然后,“如果delete表达式调用实现的回收函数...”,接着,“delete表达式将调用一个回收函数”。所以要么它不能调用重载运算符,要么它是未指明的,要么它必须调用它。以上是三种可能性之一;-) - Steve Jessop
@Hans:释放函数是实现operator delete的函数。这个术语来自于运算符出现在表达式中,而实现它们的函数就是函数。换句话说,你需要在术语上进行某种分离。 - Yakov Galka

9

operator delete就像其他运算符一样,为什么它不能被调用呢?在被调用之前,它无法检查其参数。

这就像问为什么在添加0时会调用operator+。


11
delete语句不仅会调用operator delete函数,它还会先调用析构函数,在调用析构函数之前必须进行空指针检查(因此在调用operator delete之前必须进行检查)。与其他运算符重载不同,operator delete函数有其特殊性。 - Ben Voigt
好的,但编译器无法确定当给出空指针时,用户定义的operator delete是否为no-op。例如,它可以记录一些内容,指示您尝试删除空指针。 - Tyler McHenry
2
同意Ben Voigt的观点:当您键入delete p;时,编译器首先调用对象的析构函数,然后调用释放函数operator delete。系统必须能够诊断指针是否为0,以避免调用析构函数(它不会调用),因此从技术上讲,它也可以避免调用释放器。 - David Rodríguez - dribeas
4
实际上,C++标准明确允许编译器避免使用空指针值调用 operator delete - Ben Voigt

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