"Empty classes"的 C++ 多重继承内存布局

标签 c++ multiple-inheritance memory-alignment

我知道多重继承的内存布局没有定义,所以我不应该依赖它。但是,在特殊情况下我可以依靠它吗?也就是说,一个类只有一个“真正的”父类(super class)。所有其他的都是“空类”,即既没有字段也没有虚方法的类(即它们只有非虚方法)。在这种情况下,这些额外的类不应向类的内存布局添加任何内容。 (更简洁一点,在C++11的写法中,类有standard-layout)

我可以推断所有父类(super class)都没有偏移量吗?例如:

#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;
}

这里,YX 是唯一真正的基类的类。该程序的输出(在使用 g++4.6 的 linux 上编译时)如下:

8

0x233f010

0x233f010

0x233f010

0x233f010

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

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

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

最佳答案

在 C++11 中,编译器需要为标准布局 类型使用空基类优化。见https://stackoverflow.com/a/10789707/981959

对于您的特定示例,所有类型都是标准布局类,没有公共(public)基类或成员(见下文),因此您可以依赖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 基在同一地址,但另一个 I 基是(至少对于 G++ 和 Clang++)一个字节到对象中,所以如果你有一个 I* 你不能 reinterpret_castX* 因为你不知道 哪个 I 它指向的子对象,偏移量 0 处的 II 在偏移量 1 处。

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

关于 "Empty classes"的 C++ 多重继承内存布局,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11048045/

相关文章:

python - PySide多重继承: Inheriting a QWidget and a Mixin

c++ - boost::interprocess - allocate_aligned - 在所有进程中保证相同的对齐方式?

c++ - 两个字符串的交集和并集

python - Django:使用元从抽象类多重继承

c++ - C++ 标准中最令人惊讶的元素是什么?

c++ - 警告 : defaulted move assignment operator of X will move assign virtual base class Y multiple times

numpy - 在numpy中获取页面对齐的内存

c++ - 关于 Hinnant 的栈分配器的问题

c++ - 带有模板函数名的可变参数模板

c# - 3D 点云分割