Axazeano
Axazeano

Reputation: 900

Why is sizeof(BaseClass) == sizeof(DerivedClass) although I add a member

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

Answers (4)

AnT stands with Russia
AnT stands with Russia

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

Adam D. Ruppe
Adam D. Ruppe

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

peppe
peppe

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

galop1n
galop1n

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

Related Questions