How do data members get aligned / ordered if inheritance / multiple inheritance is used? Is this compiler specific?
Is there a way to specify in a derived class how
The order of the members in memory is equal to the order in which they are specified in the program. Elements of non-virtual bases classes come before elements of the derived class. In the case of multiple inheritance, the elements of the first (left-most) class come first (and so on). Virtual base classes come last.
Each class/struct that is derived from a virtual base class has a pointer type prepended for its elements (theoretically implementation dependent).
The alignment of a class/struct is equal to the largest alignment of its members (theoretically implementation dependent).
Padding happens when the next element in memory needs it (for the sake of its alignment) (theoretically implementation dependent).
Trailing padding is added to make the size of an object a multiple of its alignment.
Complex example,
struct base1 {
char m_tag;
int m_base1;
base1() : m_tag(0x11), m_base1(0x1b1b1b1b) { }
};
struct derived1 : public base1 {
char m_tag;
alignas(16) int m_derived1;
derived1() : m_tag(0x21), m_derived1(0x1d1d1d1d) { }
};
struct derived2 : virtual public derived1 {
char m_tag;
int m_derived2_a;
int m_derived2_b;
derived2() : m_tag(0x31), m_derived2_a(0x2d2daa2d), m_derived2_b(0x2d2dbb2d) { }
};
struct derived3 : virtual public derived1 {
char m_tag;
int m_derived3;
virtual ~derived3() { }
derived3() : m_tag(0x41), m_derived3(0x3d3d3d3d) { }
};
struct base2 {
char m_tag;
int m_base2;
virtual ~base2() { }
base2() : m_tag(0x51), m_base2(0x2b2b2b2b) { }
};
struct derived4 : public derived2, public base2, public derived3 {
char m_tag;
int m_derived4;
derived4() : m_tag(0x61), m_derived4(0x4d4d4d4d) { }
};
Has the following memory layout:
derived4 = derived2 -> ....P....O....I....N....T....E....R....
subobject derived2 -> 0x31 padd padd padd 0x2d 0xaa 0x2d 0x2d
0x2d 0xbb 0x2d 0x2d padd padd padd padd
virual table = base2 -> ....P....O....I....N....T....E....R....
subobject base2 -> 0x51 padd padd padd 0x2b 0x2b 0x2b 0x2b
derived3 -> ....P....O....I....N....T....E....R....
subobject derived3 -> 0x41 padd padd padd 0x3d 0x3d 0x3d 0x3d
subobject derived4 -> 0x61 padd padd padd 0x4d 0x4d 0x4d 0x4d
derived1 = base1 -> 0x11 padd padd padd 0x1b 0x1b 0x1b 0x1b
subobject derived1 -> 0x21 padd padd padd padd padd padd padd
0x1d 0x1d 0x1d 0x1d padd padd padd padd
padd padd padd padd padd padd padd padd
Note that after casting a derived4 object to a derived2 or derived3, the new object starts with a pointer to the virtual base class, which is somewhere down below in the image of derived4, just like a real derived2 or derived3 object would.
Casting this derived4 to a base2 gives us an object that has a virtual table pointer, as it should (base2 has a virtual destructor).
The order of the elements is: first the (virtual base class pointer and) elements of derived2, then the (virtual table pointer and) elements of base, the (virtual base class pointer and) elements of derived3 and finally the elements of (the subobject of) derived4 -- all of that followed by the virtual base class derived1.
Also note that although a real 'derived3' object must be aligned at 16 bytes because it "contains" (at the end) the virtual base class derived1 which is aligned at 16, because it has a member with that is aligned at 16; but the 'derived3' that is used in the multiple inheritance here is NOT aligned at 16 bytes. This is OK, because the derived3 without the virtual base class has a max. alignment of just 8 (its virtual base class pointer; this is on a 64bit machine).