C++ 为什么有虚函数的类与没有虚函数的类的对齐方式不同?

C++ 为什么有虚函数的类与没有虚函数的类的对齐方式不同?,c++,virtual-functions,memory-alignment,memory-layout,vptr,C++,Virtual Functions,Memory Alignment,Memory Layout,Vptr,受此启发,我创建了以下代码片段以供玩弄: #include <iostream> using std::cout; using std::endl; struct erdos { void who() { cout << "erdos" << endl; } float f1; float f2; }; struct fermat : public erdos { float f3; }; struct fermat2 :

受此启发,我创建了以下代码片段以供玩弄:

#include <iostream>
using std::cout;
using std::endl;

struct erdos
{
  void who()
  {
    cout << "erdos" << endl;
  }
  float f1;
  float f2;
};

struct fermat : public erdos
{
  float f3;
};

struct fermat2 : public fermat
{
  float f4;
};

struct fermat3 : public fermat2
{
  float f5;
};

int main(void)
{
  erdos e;
  cout << "sizeof(e)" << sizeof(e) << endl;
  fermat f;
  cout << "sizeof(f)" << sizeof(f) << endl;
  fermat2 f2;
  cout << "sizeof(f2)" << sizeof(f2) << endl;
  fermat3 f3;
  cout << "sizeof(f3)" << sizeof(f3) << endl;
  cout << "sizeof(void*)" << sizeof(void*) << endl;
  cout << "sizeof(float)" << sizeof(float) << endl;
  return 0;
}
在将
virtual
添加到
who()
之后,我得到了这个

sizeof(e)16
sizeof(f)24
sizeof(f2)24
sizeof(f3)32
sizeof(void*)8
sizeof(float)4
现在,将
void*
的大小添加到结构很简单,但是为什么会在虚拟情况下而不是在非虚拟情况下有这种填充(Richard在他的演讲中也提到了这一点)

sizeof(e)16 - 8 = 8 
sizeof(f)24 - 8 = 16 but is in fact 12 (padding 4)
sizeof(f2)24 - 8 = 16 matches
sizeof(f3)32 - 8 = 24 but is in fact 20 (padding 4)
我已经用gcc 5.3.0和clang 3.7.1在Ubuntu 14.04 64位上对它进行了测试

sizeof(void*)8
这是你的答案

假设您的实现只需要一个指针来处理虚拟查找,这就是对齐的原因。在64位编译中,指针需要64位的空间(这就是为什么我们称它为“64位”)。但它也需要64位对齐


因此,在64位编译中存储指针的任何数据结构也必须是64位对齐的。对象的对齐方式必须是8字节对齐,并且大小必须填充为8字节(出于数组索引的原因)。如果将一个
float
成员作为指针,您会看到同样的情况。

您如何知道它是填充?可能是额外的数据,比如偏移量和表指针。这是一个实现细节,不能保证在所有编译器中都是相同的。您添加了一个以前不存在的虚拟方法表。因此,您的意思是指针(指向函数)必须对齐,但指向成员(如我介绍的非虚拟情况)不对齐?这就是为什么非虚拟f3的大小为
sizeof(f3)20
?@Patryk:non-virtual
f3
包含5个大小为4的成员。5×4 = 20.
float
可能是4字节对齐的。因为20可以被4整除,所以不需要填充。虚拟
f3包含5个大小为4的成员和另一个大小为8的成员。5×4+8 = 28. 
float`可能是4字节对齐的,指针可能是8字节对齐的。由于使用了更高的对齐要求,结构具有8字节的对齐。由于20不能被8平均整除,编译器需要将填充添加到8的最接近倍数,即32。当然,这只是猜测。编译器可能只是决定添加2个6字节的字段,而不需要任何对齐要求。@Revolver\u Ocelot非常感谢。这更能说明这一点topic@Patryk:另外,请注意,我和Ocelot所说的一切都是基于这样的假设,即编译器使用vtable指针来实现虚拟分派。标准中没有要求这一点;这只是一种可能的实现(尽管是一种常见的实现)。
sizeof(void*)8