为什么当我在空指针上调用“delete”时调用“operator delete”?
在阅读this question的答案时,我注意到答案(例如this)意味着即使delete
语句在空指针上执行时也可以调用operator delete
。为什么当我在空指针上调用“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;
}
和 - 令人惊讶的我 - Test::operator delete()
被调用,ptr
拿着一个空指针。
据我所知operator new
分配内存和operator delete
返回分配器的内存。如果我在空指针上调用delete
语句,则意味着指针后面没有对象,并且没有内存返回到分配器。
delete
语句包括调用析构函数。当我传递一个空指针时,析构函数肯定不会被调用 - C++负责处理这个问题。那么为什么在这种情况下调用operator delete
?
在即将到来的C++ 0x标准的语言(第5.3.5节[expr.delete]
)被调用如下:
如果 的操作数的值不是空指针值 ,则删除表达式 将调用释放函数 (3.7.4.2)。否则,未指定 函数是否会调用释放 函数。 [注意: 解除分配函数称为 ,不管对象的析构函数 还是 数组的某个元素都引发异常。 - 注完]
所以它是不确定的行为,一些编译器可以调用operator delete
当NULL指针被删除,其他人可能不会。
编辑:术语释放函数使用的标准似乎是造成一些混淆。它带有一个参考。从3.7.4.2 [basic.stc.dynamic.deallocation]
一些关键的语言可能有助于澄清:
如果一个类
T
有一个名为operator delete
恰好与一个参数一个成员释放功能,该功能是通常(非配置)释放的功能。
该标准也很清楚的是用户定义operator delete
需要接受一个参数,该参数是一个空指针值:
的 第一个参数的供应给释放函数的值可以是一个空指针值;如果是这样,并且如果释放 函数是标准库中提供的函数,则该调用不起作用。
但由于未指定的行为5.3.5,当指针为空时,不应该依赖于您调用的operator delete
。
嗯,这个片段谈论的是释放功能,而不是操作员。 – 2010-09-29 13:36:29
C++ 03在这个问题上似乎含糊不清。它说“如果删除操作数的值是空指针操作 没有效果”,那么,“如果删除表达式调用 执行释放函数...”,那么“删除表达式将调用一个释放函数“。所以要么不能调用重载操作符,要么没有指定,或者必须调用它。其中之一;-) – 2010-09-29 13:40:40
@Hans:释放函数是实现'operator delete'的函数。这个术语来自于操作符出现在表达式中的事实,而实现它们的函数是*函数。换句话说,你需要以某种方式分离你的术语。 – ybungalobill 2010-09-29 13:41:26
运算符删除就像任何其他运算符一样,为什么它不被调用?在调用之前,它无法检查其参数。
这就好比问为什么operator+
当你添加0
'delete'语句不只是调用'operator delete',它首先调用析构函数,并且在调用析构函数之前(因此在调用'operator delete'之前)必须执行一个空的检查。 'operator delete'不像其他的操作符重载。 – 2010-09-29 13:12:55
好的,但编译器无法确定在给定空指针时用户定义的操作符delete是否为空操作。例如,它可以记录表明你试图删除空指针的东西。 – 2010-09-29 13:16:32
同意Ben Voigt:当你键入'delete p;'时,编译器首先调用对象的析构函数,然后调用释放函数'operator delete'。系统必须能够诊断指针为0,以避免调用析构函数(它不会调用它),所以在技术上它也可以避免调用deallocator。 – 2010-09-29 13:19:42
问一下为什么当你分配零长度数组时,调用operator new:'new Test [0];'...;) – ybungalobill 2010-09-29 13:37:44
@ybungalobill:这很简单 - 标准要求返回的指针是有效的且不同的。 – sharptooth 2010-09-29 14:08:10
请注意,如果您使析构函数为虚拟,则不会调用重载'operator delete'。实现通常直接从析构函数调用该函数,并在不检查null的情况下调用析构函数(从而节省一些指令)。只有当呼叫需要虚拟调度时才是预先执行的检查。 – avakar 2010-09-29 14:11:10