6

我在 gcc 4.4.5 上尝试了以下代码。

如果成员“数据”不存在,则代码可以正常执行,但如果存在,则会崩溃。当派生类的 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;
}
4

2 回答 2

10

假设我的系统(gcc 4.6.0,linux x86_64)上发生的事情与您的系统上发生的事情相同(它也会崩溃data并运行),实现细节p并不指向分配的内存块的开头类型的对象derived

正如valgrind我所说,

Address 0x595c048 is 8 bytes inside a block of size 16 alloc'd

如果您打印指针的值,您可以自己看到:

derived * d = new derived;
std::cout << d << '\n';
base *p = d;
std::cout << p << '\n';

原因是 gcc 中的对象布局是 {vtable, base, derived}

当 base 为空时,{vtable, base, derived} 和 {base} 的大小恰好相同,因为分配空类的对象占用非零字节数,这两种情况下恰好相等。

当派生没有虚函数时,vtable 不存在,地址再次相同,删除成功。

于 2011-05-27T16:55:54.970 回答
2

两种类型的大小不匹配,示例中的布局应该不同。

您正在比较 pod 类型与具有 vtable 的类型(布局和偏移量是实现定义的)。当调用析构函数时,隐式 this 的地址被假定为具有 的布局base,但实际上 this 是derived。执行的内容相当于写入/读取无效地址。

于 2011-05-27T17:07:34.813 回答