当我从虚拟基础派生D时,为什么在VS2015中sizeof(D)增加了8个字节?

时间:2015-12-09 12:54:11

标签: c++ alignment c++14 virtual-inheritance

我正在使用C ++14§3.11/ 2中的示例:

struct B { long double d; };
struct D : virtual B { char c; }

在clang,g ++和VS2015

中运行下面的代码段
#include <iostream>
struct B { long double d; };
struct D : /*virtual*/ B { char c; };

int main()
{
    std::cout << "sizeof(long double) = " << sizeof(long double) << '\n';
    std::cout << "alignof(long double) = " << alignof(long double) << '\n';

    std::cout << "sizeof(B) = " << sizeof(B) << '\n';
    std::cout << "alignof(B) = " << alignof(B) << '\n';

    std::cout << "sizeof(D) = " << sizeof(D) << '\n';
    std::cout << "alignof(D) = " << alignof(D) << '\n';
}

我得到了以下结果:

                         clang           g++         VS2015  
sizeof(long double)        16             16            8
alignof(long double)       16             16            8
sizeof(B)                  16             16            8
alignof(B)                 16             16            8
sizeof(D)                  32             32           16
alignof(D)                 16             16            8

现在,在上面代码中的virtual定义中取消注释struct D并再次为clang,g ++和VS2015运行代码之后,我获得了以下结果:

                         clang           g++         VS2015  
sizeof(long double)        16             16            8
alignof(long double)       16             16            8
sizeof(B)                  16             16            8
alignof(B)                 16             16            8
sizeof(D)                  32             32           24
alignof(D)                 16             16            8

我对上面得到的结果毫不怀疑,只有一个例外:为什么VS2015中sizeof(D)从16增加到24?

我知道这是实现定义的,但是对于这种增加的大小可能有一个合理的解释。如果可能的话,这就是我想知道的。

3 个答案:

答案 0 :(得分:1)

如果您实际使用虚拟继承的虚拟方面,我认为对vtable指针的需求变得清晰。 vtable中的一个项目可能是BD开始的偏移量。

假设E几乎从BF继承ED继承,D F B最终使用E内的D作为其基类。在F的方法中,B不知道它是graphicalDisplayJFrame.getContentPane().setLayout(new LayerLayout(layerHandler));//graphicalDisplayJFrame is JFrame which represents the display 的基类怎么能找到 graphicalDisplayJPanel = new JPanel() { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g2d = (Graphics2D) g; g2d.rotate(Math.PI, anchorx, anchory); //rotation 180° } @Override public void paintChildren(Graphics g) { super.paintChildren(g); Graphics2D g2d2 = (Graphics2D) g; g2d2.rotate(Math.PI, anchorx, anchory); } }; graphicalDisplayJFrame = new JFrame(); //creation of JFrame graphicalDisplayJFrame.setContentPane(graphicalDisplayJPanel); //setting JFrame to apply rotation 的成员而没有存储在vtable中的信息?

所以clang和G ++将8个字节的填充更改为vtable指针,你认为没有变化。但是VS2015从来没有填充,所以需要为vtable指针添加8个字节。

也许编译器注意到vtable指针的唯一用途是用于计算基指针的低效方案。所以也许这可以优化为简单地使用基指针而不是vtable指针。但这不会改变对8字节的需求。

答案 1 :(得分:0)

当存在virtual基础对象时,基础对象相对于派生对象地址的位置不是静态可预测的。值得注意的是,如果您稍微扩展类层次结构,很明显可能有多个D子对象仍需要引用一个B基础对象:

class I1: public D {};
class I2: public D {};
class Most: public I1, public I2 {};

您可以通过首先转换为D*或首先转换为Most来从I1对象获取I2

Most m;
D*   d1 = static_cast<I1*>(&m);
D*   d2 = static_cast<I2*>(&m);

您将拥有d1 != d2,即真正有两个D子对象,但static_cast<B*>(d1) == static_cast<B*>(d2),即只有一个B子对象。要确定如何调整d1d2以查找指向B子对象的指针,则需要动态偏移。有关如何确定此偏移量的信息需要存储在某处。此信息的存储可能是额外8个字节的来源。

我不认为MSVC ++中类型的对象布局是[公开]记录的,即,无法确定它们正在做什么。从它的外观来看,它们嵌入了一个64位对象,以便能够分辨出基础对象相对于派生对象的地址所处的位置(指向某些类型信息的指针,指向基础的指针,对基础的偏移,某些东西)像那样)。另外8个字节最有可能源于需要存储char加上一些填充以使对象在合适的边界处对齐。这似乎与其他两个编译器所做的类似,只是它们使用16个字节开始long double(可能只有10个字节填充到合适的对齐方式)。

要了解C ++对象模型的工作原理,您可能需要查看Stan Lippman的"Inside the C++ Object Model"。它有点过时,但描述了潜在的实现技术。 MSVC ++是否使用其中任何一个我不知道,但它提供了可能使用的想法。

对于gccclang使用的对象模型,您可以查看Itanium ABI:它们主要使用Itanium ABI,并对实际使用的CPU进行微调。 / p>

答案 2 :(得分:0)

在visual studio中,默认行为是所有结构都沿着8字节边界对齐。即使你做了

struct A {
  char c;
}

然后检查sizeof(A),您将看到它是8个字节。

现在,在您的情况下,当您将struct D的继承类型更改为虚拟时,编译器必须执行额外的操作才能完成此操作。首先,它为结构D创建一个虚拟表。这个vtable包含什么?它包含一个指向内存中struct B偏移的单个指针。接下来,它在struct D的头部添加一个vptr,指向新创建的vtable。

因此,现在结构D应该看起来像:

 struct D : virtual B { void* vptr; char c; }

所以,D的大小将是:

sizeof (long double) + sizeof (void*) +  sizeof (char) = 8 + 8 + 1 = 17

这是我们在开始时讨论的边界对齐的地方。由于所有结构必须与8字节边界对齐而结构D只有17个字节,因此编译器将7个填充字节添加到结构中以使其对齐到8字节边界。

所以现在大小变成:

Size of D = Size of elements of D  + Padding bytes for byte alignment = 17 + 7 = 24 bytes.