class Widget
{
public:
Widget() {
cout<<"~Widget()"<<endl;
}
~Widget() {
cout<<"~Widget()"<<endl;
}
void* operator new(size_t sz) throw(bad_alloc) {
cout<<"operator new"<<endl;
throw bad_alloc();
}
void operator delete(void *v) {
cout<<"operator delete"<<endl;
}
};
int main()
{
Widget* w = 0;
try {
w = new Widget();
}
catch(bad_alloc) {
cout<<"Out of Memory"<<endl;
}
delete w;
getch();
return 1;
}
在此代码中,delete w
在析构函数存在时不会调用重载的delete
运算符。如果省略析构函数,则调用重载的delete
。为什么会这样?
写出Widget()时的输出
操作员新郎 内存不足
未写入Widget()时的输出
操作员新郎 记忆不足
operator delete
答案 0 :(得分:21)
我记得在comp.lang.c ++。moderated之前的类似操作符删除。我现在找不到,但答案就是这样说的......
不幸的是,语言 规格不够 明确控制是否应该进行 进入重载'运算符删除' 何时调用delete-expression 在对应的空指针上 类型,即使标准确实如此 说删除表达式 空指针是一个无操作。
James Kanze 特别说:
它仍然是响应的可靠性 operator delete(或删除[])to 校验;标准不保证 它不会被赋予空指针; 标准要求它是一个 如果给出空指针,则为no-op。或者那个 允许实现调用 它。根据最新的草案, “第一个论点的价值 提供给释放功能 可以是空指针值;如果是这样, 如果解除分配功能是 一个在标准库中提供, 电话没有效果。“我不是 确定那是什么意思 标准库中提供的一个“ 意思是---从字面上看, 因为他的功能不是一个提供的 由标准库,句子 似乎不适用。但不知何故, 这没有意义
我记得这个becoz我曾经有类似的问题,并在.txt文件中保留了答案。
<强> UPDATE-1:强>
哦,我发现它here。 另请阅读此链接defect report。 所以,答案是 未指定 。章 5.3.5 / 7 。
答案 1 :(得分:9)
首先,这可以简化为delete (Widget*)0
- 您main()
中的其他所有内容都不需要重复此操作。
这是一个代码生成假象,因为1)用户定义的operator delete
必须能够处理NULL值,2)编译器尝试生成最佳代码。
首先让我们考虑不涉及用户定义的析构函数的情况。如果是这种情况,除了operator delete
之外,没有代码可以在实例上运行。在将控制转移到operator delete
之前检查null是没有意义的,因为后者应该进行检查;所以编译器只生成operator delete
的无条件调用(你会看到后者打印一条消息)。
现在第二种情况 - 析构函数已定义。这意味着您的delete
语句实际上扩展为两个调用 - 析构函数和operator delete
。但是无法在空指针上安全地调用析构函数,因为它可能会尝试访问类字段(编译器可能会发现您的特定析构函数并没有真正执行它,因此使用null this
进行调用是安全的,但是看起来他们在实践中不打扰)。所以它在析构函数调用之前插入一个空检查。一旦检查已经存在,它也可以使用它跳过对operator delete
的调用 - 毕竟它必须是无操作的,并且它将在{{{{{{{{{{ {1}}本身,以防指针实际为空。
据我所知,ISO C ++规范无论如何都不能保证这一点。这只是两个编译器在这里进行相同的优化。
答案 2 :(得分:4)
原因是如果你有一个析构函数,那么对delete操作符的调用是在标量删除析构函数中完成的,在析构函数中,析构函数包含对析构函数和delete运算符的调用。编译器提供的代码检查您是否尝试删除NULL指针。当然,删除这样的指针是合法的,但是不能调用此类对象的析构函数,因为它可能包含成员变量的使用。为此,避免了对标量删除析构函数的调用,因此也避免了对删除操作符的调用。
当没有析构函数时,编译器只是直接调用delete运算符,而不生成标量删除析构函数。因此,在这种情况下,毕竟会调用delete运算符。
答案 3 :(得分:3)
我没有一个好的答案,但我略微简化了这个问题。以下代码删除了操作符new和异常处理:
#include <iostream>
using namespace std;
class Widget {
public:
Widget() {
cout<<"Widget()"<<endl;
}
~Widget() {
cout<<"~Widget()"<<endl;
}
void operator delete(void *v) {
cout << "operator delete" << endl;
}
};
int main() {
Widget* w = 0;
cout << "calling delete" << endl;
delete w;
}
这仍然表现出相同的行为,并且在VC ++和g ++上都是如此。
当然,删除NULL指针是一个无操作,因此编译器不必调用operator delete。如果实际分配了一个对象:
Widget* w = new Widget;
然后事情按预期工作。
答案 4 :(得分:3)
想要留下评论,而不是回答,没有足够的权限成为新成员。
在创建对象期间引发异常。 析构函数没有被调用,因为没有创建对象本身。
你也可以观察到来自构造函数&amp;的消息。析构函数没有显示出来。
但是,在未定义析构函数时会调用delete。 如果直接认为当未定义destrcutor时,C ++ Compiler将其视为任何其他运算符,则默认情况下编译器在未定义时提供析构函数。
答案 5 :(得分:-1)
在delete运算符之前调用对象析构函数。所以我的猜测是它试图调用析构函数,意识到指针是NULL因此
正如尼尔所说,如果w包含一个小部件,它应该可以工作。
答案 6 :(得分:-2)
您试图删除NULL指针。因此,析构函数没有被调用。
class Widget
{
public:
Widget()
{
cout<<"Widget()"<<endl;
}
~Widget()
{
cout<<"~Widget()"<<endl;
}
void* operator new(size_t sz) throw(bad_alloc)
{
cout<<"operator new"<<endl;
return malloc(sizeof(Widget));
//throw bad_alloc();
}
void operator delete(void *v)
{
cout<<"operator delete"<<endl;
}
};
int main()
{
Widget* w = NULL;
try
{
w = new Widget();
//throw bad_alloc();
}
catch(bad_alloc)
{
cout<<"Out of Memory"<<endl;
}
delete w;
}
输出:
操作员新郎 小工具()
〜窗口小部件()
operator delete