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)
我不会在这里问new及delete- 这更容易考虑malloc和free.在这种简单的情况下,我只是对有关派生对象布局的规定感到好奇,并且基类的初始非静态成员位于可预测的位置.
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.