From the code below sizeof(Base) == 24
and sizeof(Derived) == 24
.
Why are their sizes equal?
In Base
class we have 3 members and in Derived
class we have another member.
class Base { private: double d; protected: long l; public: int i; }; class Derived : public Base { private: float f; };
It just so happened that your class Base
has 8 byte alignment requirement, but its last member has size 4. This leads to an empty padding area added at the end of Base
's memory layout. That extra padding plays its role when you instantiate objects of class Base
by themselves, as so called most-derived objects.
Base b; // <- a most-derived object Base a[10]; // <- an array of most-derived objects
However, when you "embed" Base
as base class into class Derived
, there's no need for that extra padding at the end of the embedded Base
subobject.
Derived d; // <- object `d` contains an embedded sub-object of type `Base`
A smart compiler will attempt to reuse that area by placing the extra field of class Derived
into the layout area used for padding in Base
. In your case the extra field Derived::f
incidentally has the same size of 4 bytes, i.e. it fits in there perfectly. The end result is that the total size of the class does not increase.
A very similar (in nature) effect is so called "empty base optimization". In C++ sizeof
for any type is guaranteed to be greater than 0, which means that sizeof
of an empty class is always greater than zero. However, when you derive some other class from an empty base class, you might observe that the base class contributes exactly 0 bytes to the derived class's size. For example
struct A {}; struct B {}; struct C {}; struct D {}; struct F : A, B, C, D { int i; } int main() { std::cout << sizeof(A) << std::endl << sizeof(B) << std::endl << sizeof(C) << std::endl << sizeof(D) << std::endl; std::cout << sizeof(F) << std::endl; }
Even though sizeof
of each base class is greater than zero, sizeof(F)
will typically still evaluate to sizeof(int)
, as if base class subobjects do not exist at all.
In other words, as such examples show, base class subobjects follow noticeably more relaxed rules with regard to their memory layout than most-derived objects. These relaxed rules might easily lead to situations when sizeof
of base class will only partially contribute to sizeof
of derived class.
Because you have sizeof(double) == sizeof(long) == 8 and it often means alignof(double) is equal to 8 too. It means that Base must be size aligned on an 8 bytes boundary in case it is store in an array, and it generates a 4 bytes padding at the end, Derived removes that to put f instead.
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