I'm trying to figure it out how class alignment works in case of inheritance. Currently I understand that
So for instance:
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
};
My confusion comes in case of inheritance:
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 {
};
According to clang the layout of the class Final is:
*** 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]
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.
Is it takes the widest class in the inheritance hierarchy?
This is largely implementation-dependent. In your code:
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 doesn't have to be 8 bytes, short doesn't have to be 2 bytes, and char doesn't have to be 1 byte. If they are, then long long doesn't have to be 8-byte aligned, short doesn't have to be 2-byte aligned, and char doesn't have to be 1-byte aligned. It isn't guaranteed either that the paddings are inserted as your comments indicate. These are all implementation-defined.
Now let's see your question:
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.
If Base3 inherits from Base4, then the Base4 is a subobject of Base3, so the alignment requirement of Base4 is effectively propagated to Base3. Therefore, alignof(Base3) becomes the bigger of alignof(Base4) and the alignment requirement of its member. So, effectively, it's taking the "widest in the hierarchy." That's how real-world alignment works, at least.
Again, the standard doesn't specify this kind of things. There is no guarantee that alignof(Base3) == std::max(alignof(Base4), alignof(unsigned long)).
In the future, when you are discussing alignment, please use alignas instead of relying on the alignment of builtin types.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With