我知道多重继承的内存布局没有定义,所以我不应该依赖它。但是,在特殊情况下我可以依赖它吗?也就是说,一个类只有一个“真正的”超类。所有其他都是“空类”,即既没有字段也没有虚拟方法的类(即它们只有非虚拟方法)。在这种情况下,这些附加类不应向该类的内存布局添加任何内容。 (更简洁地说,在 C++11 的措辞中,该类有标准布局)
我可以推断所有超类都没有偏移吗?例如。:
#include <iostream>
class X{
int a;
int b;
};
class I{};
class J{};
class Y : public I, public X, public J{};
int main(){
Y* y = new Y();
X* x = y;
I* i = y;
J* j = y;
std::cout << sizeof(Y) << std::endl
<< y << std::endl
<< x << std::endl
<< i << std::endl
<< j << std::endl;
}
Here, Y
是班级X
是唯一真正的基类。程序的输出(在使用 g++4.6 的 Linux 上编译时)如下:
8
0x233f010
0x233f010
0x233f010
0x233f010
正如我的结论,没有指针调整。但是这个实现是特定的还是我可以依赖它。即,如果我收到一个类型的对象I
(而且我知道只存在这些类),我可以使用reinterpret_cast
将其投射到X
?
我希望我可以依赖它,因为规范规定对象的大小必须至少是一个字节。因此,编译器无法选择其他布局。如果会布局的话I
and J
成员背后X
,那么它们的大小将为零(因为它们没有成员)。因此,唯一合理的选择是将所有超类无偏移地对齐。
如果我使用reinterpret_cast,我是正确的还是在玩火?I
to X
here?
在 C++11 中,编译器需要使用空基类优化标准布局类型。看https://stackoverflow.com/a/10789707/981959
对于您的特定示例,所有类型都是标准布局类,并且没有公共基类或成员(见下文),因此您可以依赖该行为in C++11(实际上,我认为许多编译器已经遵循了该规则,当然 G++ 也遵循了该规则,其他编译器也遵循了安腾 C++ ABI.)
警告:确保您没有任何相同类型的基类,因为它们必须位于不同的地址,例如
struct I {};
struct J : I {};
struct K : I { };
struct X { int i; };
struct Y : J, K, X { };
#include <iostream>
Y y;
int main()
{
std::cout << &y << ' ' << &y.i << ' ' << (X*)&y << ' ' << (I*)(J*)&y << ' ' << (I*)(K*)&y << '\n';
}
prints:
0x600d60 0x600d60 0x600d60 0x600d60 0x600d61
对于类型Y
只有其中之一I
基数可以在偏移量为零处,所以尽管X
子对象位于偏移量零处(即offsetof(Y, i)
为零)和其中之一I
基址位于同一地址,但另一个I
基数(至少对于 G++ 和 Clang++)是对象中的一个字节,所以如果你有一个I*
你不能reinterpret_cast
to X*
因为你不会知道which I
它指向的子对象I
在偏移量 0 或I
在偏移量 1 处。
编译器把第二个放进去就可以了I
偏移量 1 处的子对象(即inside the int
) 因为I
没有非静态数据成员,因此您实际上无法取消引用或访问该地址处的任何内容,只能获取指向该地址处的对象的指针。如果您将非静态数据成员添加到I
then Y
将不再是标准布局并且不必使用 EBO,并且offsetof(Y, i)
将不再为零。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)