size 和align 是用作完整类型时类的大小和对齐方式。也就是说,如果您创建的对象的完整类型就是该类型(例如定义该类型的变量,或者将该类型与 new 一起使用)。
大小只是它占用的字节数。所以size=16表示当作为完整类型使用时,它总是占用16个字节。
对齐告诉你对象可以放在哪里:align=8 表示对象的地址必须是 8 的整数倍。
base size 和base align 给出大小和对齐方式,以防该类用作基类。它们不同的原因在于,C++ 标准允许对象在用作基类时使用较少的填充。
让我们具体看一下您的示例(我假设您在第一种情况下实际上在double 之前有int)。我也省略了public 和private,因为在这里它们不会改变任何东西(如果你有公共或私有数据成员,他们可以原则上改变一些东西,但我没有'不知道是否有任何编译器利用了这一点)。我也在猜测int 和double 的大小和对齐方式(实际上我假设的值是很常见的选择,并解释你得到的值)。
所以在第一种情况下(我假设)你有
class A
{
int m_number;
double m_nothing;
};
现在int 的大小和对齐方式为4,而double 的大小和对齐方式为8。
让我们完成编译器的工作并构建我们的类。
首先,我们有m_number,它占用4个字节。我们必须按照给定的顺序排列成员,所以m_number 位于A 的开头:
iiii
到目前为止,我们有大小 4(int 的四个字节)和对齐方式 4(因为 int 具有对齐方式 4)。但是现在我们必须添加一个双精度(大小和对齐方式 8)。由于直接在 int 之后,我们位于(相对)地址 4,我们没有正确对齐 double,所以我们必须添加 4 个 padding 字节(我将用* 标记)得到 8 的倍数。因此我们得到了我们的类:
iiii****dddddddd
现在,如果该类被用作基类,我们就完成了。因此,我们需要 base size=16 和 base align=8(我们需要对齐 8 才能正确对齐双精度)。
对于完整的对象,还有另一个考虑因素:标准要求在数组中,对象相互跟随,中间没有间隙。也就是说,对象之后的第一个字节必须为下一个对象正确对齐。这最终意味着完整对象的大小必须是其对齐方式的倍数。
现在我们发现的对象布局已经满足了这个要求。因此,我们也可以将它原封不动地用于整个对象。因此,我们得到了完整对象的size=16 和align=8。
现在考虑顺序颠倒的情况:
class A
{
double m_nothing;
int m_number;
};
现在我们必须从double开始:
dddddddd
接下来,我们必须添加int。事实证明,下一个空闲位置已经正确对齐 int,因此我们可以附加它:
ddddddddiiii
现在作为基础对象,我们准备好了。如您所见,我们只需要 12 个字节,因此 base size=12。当然,要正确对齐double,对象又必须从 8 的倍数的地址开始。因此我们有 base align=8。
但是,对于作为完整对象的 sue,我们现在发现下一个地址将位于位置 12,对于 double 成员未正确对齐。因此,我们必须添加填充字节,直到我们再次到达正确对齐的地址:
ddddddddiiii****
如您所见,现在我们需要 16 个字节,即size=16。由于双倍,我们仍然有align=8。
请注意,对齐要求会极大地影响类的大小。例如考虑以下两种类型:
struct S1
{
char c1;
double d1;
char c2;
double d2;
char c3;
};
struct S2
{
double d1;
double d2;
char c1;
char c2;
char c3;
};
虽然两者都包含相同的成员,但 S1 的大小和对齐方式的总(非基本)大小为 40,而 S2 的总大小仅为 24。事实上,键入S1 将作为完整对象看起来像
c*******ddddddddc*******ddddddddc*******
而S2 类型的那些看起来像
ddddddddddddddddccc*****
所以底线是具有最高对齐要求的成员应该始终排在第一位。
还要注意sizeof返回完整对象的大小,即类层次转储调用size的大小。