Difference between class and struct with regards to padding and inheritance
This all boils down to whether your type is an aggregate or not. With
struct Base {
Base() {}
double foo;
int bar;
};
struct Derived : public Base {
int baz;
};
Base
is not an aggregate because of the constructor. When you remove the constructor, you make Base
an aggregate which, per Adding a default constructor to a base class changes sizeof() a derived type, means gcc won't "optimize" for space and the derived object won't use the base's tail padding.
When you change the code to
class Base {
double foo;
int bar;
};
struct Derived : public Base {
int baz;
};
foo
and bar
are now private (becauses classes have private accessibility by default) which again means Base
is no longer an aggregate as aggregates are not allowed to have private members. This means we are back to how the first case works.
With your Base class you will get 4 bytes of tail padding, and the same with the Derived class, that's why it should normally be 24 bytes
total for the size of Derived
.
It becomes 16 bytes, because your compiler is able to do tail padding reuse.
However tail padding reuse is problematic with POD
types (all members public, defaulted constructor, etc...), because it breaks common assumptions a programmer would make. (So basically any sane compiler won't do tail padding reuse for pod types)
Let's pretend compilers would use the tail padding reuse
for POD types:
struct Base {
double foo;
int bar;
};
struct Derived : Base {
int baz;
};
int main(int argc, char** argv)
{
// if your compiler would reuse the tail padding then the sizes would be:
// sizeof(Base) == 16
// sizeof(Derived) == 16
Derived d;
d.baz = 12;
// trying to zero *only* the members of the base class,
// but this would zero also baz from derived, not very intuitive
memset((Base*)&d, 0, sizeof(Base));
printf("%d", d.baz); // d.baz would now be 0!
}
When adding an explicit constructor to the Base class, or by changing the struct
keywords to class
, the Derived
class doesn't satisfy the POD definition anymore and therefore tail padding reuse doesn't happen.