我在gcc 4.4.5上尝试了以下代码。
如果成员'data'不存在,代码执行正常,但在其存在时,它会崩溃。当派生类'dtor不是虚拟的时,它也不会崩溃。
我知道在这两种情况下行为都是未定义的,如C ++ 03(5.3.5 / 3)中所列,但仍有人可以向我提供一些解释为什么会这样做在后一种情况下会崩溃吗?
是的,我知道UB意味着任何事情都可能发生,但仍然我想知道特定于实现的细节。
#include<iostream>
using std::cout;
struct base{
int data;
base(){
cout << "ctor of base\n";
}
~base(){
cout << "dtor of base\n";
}
};
struct derived : base{
derived(){
cout << "ctor of derived\n";
}
virtual ~derived(){
cout << "dtor of derived\n";
}
};
int main(){
base *p = new derived;
delete p;
}
答案 0 :(得分:10)
假设我的系统上发生了什么(gcc 4.6.0,linux x86_64)与你的系统发生的情况相同(它也与data
崩溃并且没有运行),实现细节是{{1} } not 指向为p
类型的对象分配的内存块的开头。
正如derived
告诉我的那样,
valgrind
如果你打印指针的值,你可以自己看看:
Address 0x595c048 is 8 bytes inside a block of size 16 alloc'd
原因是gcc中的对象布局是{vtable,base,derived}
当base为空时,{vtable,base,derived}和{base}的大小恰好相同,因为分配空类的对象占用非零字节数,在两种情况下恰好相同。 / p>
当派生没有虚函数时,vtable不存在,地址再次相同,删除成功。
答案 1 :(得分:2)
两种类型的大小不匹配,示例中的布局应该不同。
您正在比较pod类型与具有vtable的类型(布局和偏移是实现定义的)。当调用析构函数时,假定隐式this的地址具有base
的布局,但这实际上是derived
。执行的内容相当于写入/读取无效地址。