系统相关
首页 > 系统相关> > 带有“空类”的C多重继承内存布局

带有“空类”的C多重继承内存布局

作者:互联网

我知道多继承的内存布局没有定义,所以我不应该依赖它.但是,我可以在特殊情况下依赖它.也就是说,一个班级只有一个“真正的”超级班级.所有其他的都是“空类”,即既没有字段也没有虚拟方法的类(即它们只有非虚方法).在这种情况下,这些附加类不应该向类的内存布局添加任何内容. (更简洁地说,在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;
}

在这里,Y是一个类,X是唯一真正的基类.程序的输出(当使用g 4.6在linux上编译时)如下:

8

0x233f010

0x233f010

0x233f010

0x233f010

我总结说,没有指针调整.但是这个实现是特定的还是我可以依赖它.即,如果我收到类型I的对象(我知道只存在这些类),我可以使用reinterpret_cast将其强制转换为X吗?

我希望我可以依赖它,因为规范说对象的大小必须至少是一个字节.因此,编译器无法选择其他布局.如果它将I和J布置在X的成员后面,那么它们的大小将为零(因为它们没有成员).因此,唯一合理的选择是在没有偏移的情况下对齐所有超类.

如果我在这里使用从I到X的reinterpret_cast,我是正确还是在玩火?

解决方法:

在C 11中,编译器需要对标准布局类型使用Empty Base-class Optimization.见https://stackoverflow.com/a/10789707/981959

对于您的具体示例,所有类型都是标准布局类,并且没有公共基类或成员(见下文),因此您可以依赖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';

}

打印:

0x600d60 0x600d60 0x600d60 0x600d60 0x600d61

对于类型Y,只有I个碱基中的一个可以在偏移零处,因此尽管X子对象在偏移零处(即offsetof(Y,i)为零)并且I个碱基中的一个位于相同的地址,但是我的另一个基础是(至少用G和Clang)一个字节进入对象,所以如果你有一个I *你就不能将它重新解释为X *,因为你不知道它指向的是哪个子对象,偏移0处的I或偏移1处的I.

编译器将第二个I子对象放在偏移量1(即int内)是可以的,因为我没有非静态数据成员,所以你实际上不能取消引用或访问该地址的任何东西,只能得到一个指针到该地址的对象.如果您向我添加了非静态数据成员,则Y将不再是标准布局,并且不必使用EBO,并且offsetof(Y,i)将不再为零.

标签:c,memory-alignment,multiple-inheritance
来源: https://codeday.me/bug/20191006/1862041.html