继承中的内存对齐规则

3

我正在尝试理解继承中的类对齐方式。目前我了解到:

  • 类的对齐方式取决于最宽的成员变量
  • 如果类是数组的一部分,那么其末尾可以根据对齐方式进行填充
  • 结构体成员变量的对齐方式遵循结构体的对齐方式

举个例子:

struct Nested { // Due to the long long element it is 8 aligned
    short int ShortNestedElement; //6 bytes of padding here        
    long long int LongNestedElement;
};

struct Example {
    short int ExampleMember_1; // 6 bytes of padding here
    Nested ExampleMember_2;
    char ExampleMember_2; // 7 bytes of padding here  
};

我在继承方面感到困惑:

struct Base1 {
    short unsigned int Base_1Member_1;
    long unsigned int Base_1Member_2;
};

struct Base2 {
    unsigned int Base_2Member_1;
};


struct Base4 {
    unsigned int Base_4Member_1;
};

struct Base3 : Base4 {
    long unsigned int Base_3Member_1;
};

struct Final: Base1, Base2, Base3 {
};

根据clang,类Final的布局为:

*** Dumping AST Record Layout
         0 | struct Final
         0 |   struct Base1 (base)
         0 |     unsigned short Base_1Member_1
         8 |     unsigned long Base_1Member_2
        16 |   struct Base2 (base)
        16 |     unsigned int Base_2Member_1
        24 |   struct Base3 (base)         --------> ? 
        24 |     struct Base4 (base)
        24 |       unsigned int Base_4Member_1
        32 |     unsigned long Base_3Member_1
           | [sizeof=40, dsize=40, align=8,
           |  nvsize=40, nvalign=8]

我不明白为什么在考虑Base3继承自Base4时(因此Base4的成员出现在Base3之前),我们会考虑Base3(8字节)的对齐而不是Base4(4字节)的对齐。但是,如果我更改这两个类的内容,使Base3对齐到4并且Base4对齐到8,则它将采用Base4的对齐方式。

是以继承层次结构中最宽的类为准吗?


1
我不确定这里是否有任何保证。我认为它是“实现定义”的,但我不太确定。 - Jesper Juhl
3
在派生类的对象表示中,每个直接和间接的基类都作为基类子对象以 实现定义 偏移存在。 - Richard Critten
1个回答

1
这在很大程度上取决于实现。在你的代码中:
struct Nested { // Due to the long long element it is 8 aligned
    short int ShortNestedElement; //6 bytes of padding here        
    long long int LongNestedElement;
};

struct Example {
    short int ExampleMember_1; // 6 bytes of padding here
    Nested ExampleMember_2;
    char ExampleMember_2; // 7 bytes of padding here  
};
"long long"不一定是8个字节,"short"不一定是2个字节,"char"不一定是1个字节。如果它们是这样的话,那么"long long"不一定是8字节对齐,"short"不一定是2字节对齐,"char"不一定是1字节对齐。也不能保证填充会按照您的注释所示插入。所有这些都是"实现定义"。
现在让我们看看您的问题:
struct Base1 {
    short unsigned int Base_1Member_1;
    long unsigned int Base_1Member_2;
};

struct Base2 {
    unsigned int Base_2Member_1;
};


struct Base4 {
    unsigned int Base_4Member_1;
};

struct Base3 : Base4 {
    long unsigned int Base_3Member_1;
};

struct Final: Base1, Base2, Base3 {
};

I don't understand why, when we consider Base3 even if it inherits from Base4 (so the members of Base4 comes before Base3) we consider the alignment of Base3(8) instead of Base4(4). But if I change the contents of these two classes so that Base3 is aligned to 4 and Base4 is aligned to 8 it takes the alignment of Base4.

如果Base3继承自Base4,那么Base4就是Base3的子对象,所以Base4的对齐要求有效地传播到Base3。因此,alignof(Base3)变成了alignof(Base4)和其成员的对齐要求中更大的那个。因此,实际上它取的是“层次结构中最宽的”。至少在现实世界中,这就是对齐的工作方式。
同样,标准并没有指定这种类型的内容。不能保证alignof(Base3) == std::max(alignof(Base4), alignof(unsigned long))
在将来讨论对齐方式时,请使用alignas代替依赖于内置类型的对齐方式。

谢谢您的回复。这种传播通常适用于更大的层次结构吗?我的意思是,对齐要求从最远的基类传播到派生类? - Minee
这是按层次树来的吗?我的意思是对齐要求是按照层次树分开的吗?比如说,Base1有它自己的层次树,Base2和Base3也有,那么这个“规则”是分别应用于这些树的吗? - Minee
1
@Minee 对于第一个评论,答案是(实际上)是的。对于第二个评论,我认为你是正确的。Base1本身与Base3无关,因此它们的对齐要求(实际上)是分别确定的。 - L. F.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接