P ++中的POD和继承11.结构的地址=第一个成员的地址吗?

Aar*_*aid 7 c++ language-lawyer c++11

(我编辑了这个问题是为了避免干扰.在任何其他问题有意义之前,有一个核心问题需要澄清.对现在的答案似乎不太重要的任何人道歉.)

让我们建立一个具体的例子:

struct Base {
    int i;
};
Run Code Online (Sandbox Code Playgroud)

没有虚方法,也没有继承,通常是一个非常愚蠢和简单的对象.因此,它是普通旧数据(POD),它可以回溯到可预测的布局.特别是:

Base b;
&b == reinterpret_cast<B*>&(b.i);
Run Code Online (Sandbox Code Playgroud)

这是根据维基百科(它本身声称参考C++ 03标准):

指向一个POD结构对象,适宜地转换使用重新解释铸造,点到它的初始构件,反之亦然,这意味着有在POD结构的开头没有填充.[8]

现在让我们考虑继承:

struct Derived : public Base {
};
Run Code Online (Sandbox Code Playgroud)

同样,没有虚方法,没有虚继承,也没有多重继承.因此这也是POD.

问题:这个事实(Derived是C++ 11中的POD)是否允许我们这样说:

Derived d;
&d == reinterpret_cast<D*>&(d.i); // true on g++-4.6
Run Code Online (Sandbox Code Playgroud)

如果这是真的,那么以下将是明确定义的:

Base *b = reinterpret_cast<Base*>(malloc(sizeof(Derived)));
free(b); // It will be freeing the same address, so this is OK
Run Code Online (Sandbox Code Playgroud)

我不会在这里问newdelete- 这更容易考虑mallocfree.在这种简单的情况下,我只是对有关派生对象布局的规定感到好奇,并且基类的初始非静态成员位于可预测的位置.

Derived对象应该等效于:

struct Derived { // no inheritance
    Base b; // it just contains it instead
};
Run Code Online (Sandbox Code Playgroud)

事先没有填充?

Ben*_*igt 14

你不关心POD-ness,你关心标准布局.这是定义,来自标准第9节[class]:

标准布局类是一个类:

  • 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
  • 没有虚函数(10.3),也没有虚基类(10.1),
  • 对所有非静态数据成员具有相同的访问控制(第11条),
  • 没有非标准布局基类,
  • 或者在最派生类中没有非静态数据成员,并且最多只有一个具有非静态数据成员的基类,或者没有具有非静态数据成员的基类,并且
  • 没有与第一个非静态数据成员相同类型的基类.

然后保证您想要的房产(第9.2节[class.mem]):

指向标准布局结构对象的指针(适当地使用a转换)reinterpret_cast指向其初始成员(或者如果该成员是位字段,则指向它所驻留的单元),反之亦然.

这实际上比旧的要求更好,因为reinterpret_cast通过添加非平凡的构造函数和/或析构函数不会丢失.


现在让我们转到你的第二个问题.答案不是你所希望的.

Base *b = new Derived;
delete b;
Run Code Online (Sandbox Code Playgroud)

除非Base有虚拟析构函数,否则是未定义的行为.见5.3.5([expr.delete])

在第一个备选(删除对象)中,如果要删除的对象的静态类型与其动态类型不同,则静态类型应为要删除的对象的动态类型的基类,静态类型应具有虚拟析构函数或行为未定义.


你早先段使用malloc,并free大多是正确的.这将有效:

Base *b = new (malloc(sizeof(Derived))) Derived;
free(b);
Run Code Online (Sandbox Code Playgroud)

因为指针的值b与放置new的地址相同,而new又从返回的地址返回malloc.