gex*_*ide 8 c++ multiple-inheritance memory-alignment
我知道多继承的内存布局没有定义,所以我不应该依赖它.但是,我可以在特殊情况下依赖它.也就是说,一个班级只有一个"真正的"超级班级.所有其他都是"空类",即既没有字段也没有虚拟方法的类(即它们只有非虚方法).在这种情况下,这些附加类不应该向类的内存布局添加任何内容.(更简洁地说,在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;
}
Run Code Online (Sandbox Code Playgroud)
这里,Y类X是唯一真正的基类.程序的输出(在linux上用g ++ 4.6编译时)如下:
8
0x233f010
0x233f010
0x233f010
0x233f010
我总结说,没有指针调整.但是这个实现是特定的还是我可以依赖它.即,如果我收到一个类型的对象I(我知道只有这些类存在),我可以使用a reinterpret_cast来将其转换为X?
我希望我可以依赖它,因为规范说对象的大小必须至少是一个字节.因此,编译器无法选择其他布局.如果它将布局I并J在成员后面X,那么它们的大小将为零(因为它们没有成员).因此,唯一合理的选择是在没有偏移的情况下对齐所有超类.
如果我使用reinterpret_cast I来到X这里,我是正确的还是我正在玩火?
Jon*_*ely 10
在C++ 11中,编译器需要对标准布局类型使用Empty Base-class Optimization .请参阅/sf/answers/755279521/
对于您的特定示例,所有类型都是标准布局类,并且没有公共基类或成员(见下文),因此您可以依赖于C++ 11中的这种行为 (在实践中,我认为许多编译器已遵循该规则当然G ++做了,其他人跟随Itanium 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';
}
Run Code Online (Sandbox Code Playgroud)
打印:
0x600d60 0x600d60 0x600d60 0x600d60 0x600d61
Run Code Online (Sandbox Code Playgroud)
对于类型,Y只有一个I基数可以偏移零,所以尽管X子对象偏移零(即offsetof(Y, i)为零),其中一个I基数位于同一地址,但另一个I基数是(至少用G ++)和Clang ++)一个字节进入对象,所以如果你有一个I*你不能reinterpret_cast,X*因为你不知道它指向哪个 I子对象,Iat偏移0或I偏移1.
这是确定的编译器把第二I子对象的偏移量1(即内部的int),因为I没有非静态数据成员,所以你不能真正取消引用或在该地址访问什么,只能获得一个指向对象在那个地址.如果您添加了非静态数据成员,I那么Y它将不再是标准布局,并且不必使用EBO,并且offsetof(Y, i)不再为零.