4
#pragma pack(push, 4)
class Father
{
 public:
  int b;
  char c;
};
class Child : public Father
{
  char e;

};
#pragma pack(pop)

sizeof(Father)=8 sizeof(Child)=12
但是如果我们像这样改变父类:

class Father
{
 private:// change from public
  int b;
  char c;
};

大小(儿童)=8

4

3 回答 3

5

这是编译器的实现细节。换句话说,不是你的业务,除非你真的,真的需要让你的数据尽可能小。注意这里的过早优化。

最后,它可能归结为 Common C++ ABI 的特性,使用诸如“用于布局目的的 POD”和“基类填充重用”之类的术语。

编辑:或者不是,因为这些编译指示建议您使用 Visual Studio。在这种情况下,永远不要忘记 MS ABI 是一个疯狂的向后兼容性黑客丛林。

于 2013-09-17T09:55:38.583 回答
3

您看到的大小差异是与 C 兼容的结果。C 只知道struct所有内容都在哪里公开。因此,当您public在 C++struct或中使用成员时class,ABI 与 C 兼容,因此它需要遵循 C 的对齐规则。

声明 membersprivate时,C++ ABI 可以更自由地优化成员的打包。

于 2013-09-17T09:58:37.960 回答
2

首先,您使用的是 a #pragma,这可能会使您的编译器不标准;我不知道它在这里可能会产生什么影响,但这肯定意味着编译器可能会做一些奇怪的事情(包括以与任何其他接口完全不兼容的方式布置类)。

#pragma话虽如此,无论有没有: 使用 g++ ,我都会得到相同的结果 ,这些结果反映了你的结果;使用 VC++,派生类在所有情况下都得到 12。首先要注意的是,无论是private还是public,以及无论是 g++ 还是 VC++,基类的大小始终为 8。这是出于对齐考虑:两个编译器都尝试int 在 4 的倍数上保持对齐,并且为了做到this 在 的数组中Father, 的总大小Father必须为 8,在 . 之后有三个字节的填充c。由于Child还包含(通过继承间接地) an int,因此适用相同的对齐注意事项。乍一看,我们期望 Child大小为 12:8 个字节Father, 加上 1 个字节的数据,加上 3 个字节的对齐填充。VC++ 就是这种情况。但是,C++ 标准允许一种叫做“空基类优化”的东西(因为它最初是为了让空基类不占用内存而设计的):它说的是当用作基类时,派生类可以重用任何填充在基地。所以Child可以将它的成员e放在 + 5 的地址Father(因为那是填充Father开始的地方),因此只需要 6 个字节(出于对齐原因,向上舍入为 8)。

为什么VC++不做这个优化,我不知道;也许他们受到优化名称的影响。(当基类没有数据成员时,他们会这样做。)为什么 g++ 只有在有私有成员而不是公共成员时才会这样做,这更奇怪,但标准并不要求它。

请注意,当应用优化时,类似于:

Father* p1 = new Child;
Father* p2 = new Child;
memcpy( p2, p1, sizeof(Father) );

可能会产生令人惊讶的后果,实际占用的空间大小Father可能比所sizeof 指示的要小。这可能解释了选择 g++ 背后的逻辑:memcpy如果类具有私有成员则无效,因此他们可以应用它;如果所有成员都是公共的(以及其他一些条件),则它是有效的,因此他们不应用它,以避免破坏上述内容。(添加一个构造函数,这也是memcpy非法的,导致 g++ 应用优化。)

于 2013-09-17T11:07:45.897 回答