Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MSVC Object Layout Quirk

I have a simple class in C++ that has an integer and a vtable:

class Something {

   virtual void sampleVirtualMethod();

   int someInteger;
};

If you look at the object layout for MSVC (using the /d1reportSingleClassLayout) you get:

class Something       size(8):
        +---
 0      | {vfptr}
 4      | someInteger
        +---

Which makes complete sense. 4 bytes for the vtable pointer and 4 bytes for the integer. The weird thing is when I add a double to the class:

class Something {    
    virtual void sampleVirtualMethod();
    int someInteger;
    **double someDouble;**
};

I get this object layout:

class Something       size(24):
        +---
 0      | {vfptr}
 8      | someInteger
        | <alignment member> (size=4)
16      | someDouble
        +---

Why is the difference between the 0 offset and someInteger 8 instead of 4? Did the vtable grow to 8 bytes somehow? No matter the order of when I add a double, this happens.

Thanks.

like image 958
Mason Avatar asked Feb 12 '10 09:02

Mason


2 Answers

This blog post discusses the same problem and includes an explanation in the comments by Jan Gray, who wrote the layout code MS C++ compiler a long time ago.

To paraphrase, the vfptr is inserted into the class layout only after the other data members have been laid out. Depending on the alignment requirements of the data members, this means that unnecessary padding may be introduced. Also this only happens if the class does not have a base class with a vfptr.

Hence the workaround, that is presented in the blog post:

class EmptyBase
{
protected:
    virtual ~EmptyBase() {}
};

class Something : public EmptyBase {    
    virtual void sampleVirtualMethod();
    int someInteger;
    **double someDouble;**
};

sizeof(Something) should be 16 in this case.

like image 149
honggoff Avatar answered Oct 10 '22 08:10

honggoff


I suspect that this answer has something to do with it. To quote from dirkgently's answer, quoting the GCC manual:

Note that the alignment of any given struct or union type is required by the ISO C standard to be at least a perfect multiple of the lowest common multiple of the alignments of all of the members of the struct or union in question.

According to that rule, once you've added an 8-byte double, the compiler has to arrange everything on 8-byte multiples. You can override that with #pragma pack(), of course, although it will be less efficient if you end up with an 8-byte member on something other than an 8-byte boundary.

like image 23
aalpern Avatar answered Oct 10 '22 08:10

aalpern