Reputation: 900
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;
};
Upvotes: 40
Views: 2801
Reputation: 320531
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.
Upvotes: 51
Reputation: 25595
Padding due to alignment needs:
Although the compiler (or interpreter) normally allocates individual data items on aligned boundaries, data structures often have members with different alignment requirements. To maintain proper alignment the translator normally inserts additional unnamed data members so that each member is properly aligned. In addition the data structure as a whole may be padded with a final unnamed member. This allows each member of an array of structures to be properly aligned.
More on this here:
http://en.wikipedia.org/wiki/Data_structure_alignment#Data_structure_padding
Upvotes: 2
Reputation: 22744
Use pahole to figure it out:
class Base {
private:
double d; /* 0 8 */
protected:
long int l; /* 8 8 */
int i; /* 16 4 */
/* size: 24, cachelines: 1, members: 3 */
/* padding: 4 */
/* last cacheline: 24 bytes */
};
class Derived : public Base {
public:
/* class Base <ancestor>; */ /* 0 24 */
/* XXX last struct has 4 bytes of padding */
private:
/* Bitfield combined with next fields */
float f; /* 20 4 */
/* size: 24, cachelines: 1, members: 2 */
/* paddings: 1, sum paddings: 4 */
/* last cacheline: 24 bytes */
};
Upvotes: 5
Reputation: 8824
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.
Upvotes: 9