为什么sizeof(Base)与sizeof(Derived)没有不同

时间:2019-03-27 11:20:09

标签: c++ sizeof virtual-functions memory-layout vptr

我认为sizeof(Base)应该是12。为什么是16?

没有虚函数,我得到4和8。

class Base{
  public:
    int i;
    virtual void Print(){cout<<"Base Print";}
};

class Derived:public Base{
  public:
    int n;
    virtual void Print(){cout<<"Derived Print";}
};

int main(){
  Derived d;
  cout<<sizeof(Base)<<","<<sizeof(d);
  return 0;
}

预期结果:12,16

实际结果:16,16

2 个答案:

答案 0 :(得分:8)

  

为什么sizeof(Base)与sizeof(Derived)没有不同

由于编译器引入的 alignment

那是与体系结构有关的,但是为了简单起见,我假设我们指的是64位体系结构。

Scenario 64 bit / Clang 8.0

Base类型的对齐方式是8个字节:

alignOfBase(): # @alignOfBase()
  mov eax, 8
  ret

Base的布局由变量成员(int)和虚拟表(vtptr)组成。

如果我们假设一个“通用”架构,那么:

  • int是4个字节的大小。
  • vtptr是一个指针。在64位架构上,字节大小为8个字节。

如您所料,我们应该有4 + 8 = 12的总和。

但是,我们需要记住Base的对齐方式是8 bytes。因此,连续的Base类型应该存储在8的位置倍数中。

为了保证这一点,编译器引入了Base的填充。这就是Base为16字节大小的原因。

例如,如果我们考虑2个连续的Basebase0base1)没有填充:

0:  vtptr (base 0) + 8
8:  int   (base 0) + 4
12: vtptr (base 1) + 8  <--- Wrong! The address 12 is not multiple of 8.
20: int   (base 1) + 4

带有填充:

0:  vtptr (base 0) + 8
8:  int   (base 0) + 4+4   (4 padding)
16: vtptr (base 1) +8      <--- Fine! The adress 16 is multiple of 8.
24: int   (base 1) +4+4    (4 padding)

Derived类型也有同样的故事。

Derived的布局应为:vtptr + int + int,即8 + 4 + 4 = 16

Derived的对齐方式也是8

alignOfDerived(): # @alignOfDerived()
  mov eax, 8
  ret

实际上,在这种情况下,无需引入填充即可使Derived与内存对齐。布局尺寸将与实际尺寸相同。

0:   vtptr (Derived 0)
8:   int   (Derived 0)
12:  int   (Derived 0)
16:  vtptr (Derived 1)  <---- Fine. 16 is multiple of 8.
24:  int   (Derived 1)
28:  int   (Derived 1)

答案 1 :(得分:1)

发生这种情况是由于编译器决定对齐您的类。

如果您希望(或需要)结构或类具有其“实际”大小,则可以像这样使用#pragma pack(1)

#pragma pack(push, 1) // Set packing to 1 byte and push old packing value to stack

class Base{
  public:
    int i;
    virtual void Print(){cout<<"Base Print";}
};

class Derived:public Base{
  public:
    int n;
    virtual void Print(){cout<<"Derived Print";}
};

int main(){
  Derived d;
  cout<<sizeof(Base)<<","<<sizeof(d);
  return 0;
}

#pragma pack(pop) // restore old packing value