MIKU_LINK_SUCCESS
MIKU_LINK_SUCCESS

Reputation: 43

Why VS compiler insert a NULL into the object memory

class RootBase{
public:
    RootBase():ai(12){}
    virtual void fas(){
        printf("%p \n", this);
    }
private:
    int ai;

};

class Base : virtual public RootBase{

public:
    Base():bai(1){}

    virtual void fas(){

    }
    virtual void fa(){

    }
private:
    int bai;
};

Base *pBase = new Base; 

I test the virtual inheritance relationship case about memory location of class. The result surprise me, for the size of Base is 24 Bytes. I can understand the 20 Bytes which include the virtual function table pointer, virtual base table pointer and the member variable.

The memory distribution of the Base object pointed by pBase is:

 60 77 41 00 68 77 41 00 01 00 00 00 00 00 00 00 54 77 41 00 0c 00 00 00

But I can not understand the meaning of the 13th - 16th Bytes , 0x00000000 ,a NULL(integer 0 in x86 32bit) . What does the four Bytes 0 do? (OS : win xp , IDE :visual studio 8.0)

I also think that the integer 0 represent the alignment padding , but there is no reason to pad to 8 bytes.

Upvotes: 1

Views: 405

Answers (1)

WhozCraig
WhozCraig

Reputation: 66234

This taken directly from a dump of the object layout for the classes you're describing. I hope it answers your question. From a little more research I discovered that vtordisp for class Base. is an offset used when calling virtual functions from either the constructor or destructor of a class declared with a virtual base. Apparently its to inform them where your virtual function table is.

From Jonathan Caves, MSFT, over 5 years ago:

It is used very rarely - but we have to add it to classes that inherit from a virtual base class and override virtual functions just in case the user does call a virtual function in the constructor or destructor.

Note: Sorry about the 1> preamble on all the lines. Blame Microsoft, its their tools.

1>  class RootBase  size(8):
1>      +---
1>   0  | {vfptr}
1>   4  | ai
1>      +---
1>  
1>  RootBase::$vftable@:
1>      | &RootBase_meta
1>      |  0
1>   0  | &RootBase::fas
1>  
1>  RootBase::fas this adjustor: 0
1>  
1>  
1>  class Base  size(24):
1>      +---
1>   0  | {vfptr}
1>   4  | {vbptr}
1>   8  | bai
1>      +---
1>  12  | (vtordisp for vbase RootBase)
1>      +--- (virtual base RootBase)
1>  16  | {vfptr}
1>  20  | ai
1>      +---
1>  
1>  Base::$vftable@Base@:
1>      | &Base_meta
1>      |  0
1>   0  | &Base::fa
1>  
1>  Base::$vbtable@:
1>   0  | -4
1>   1  | 12 (Based(Base+4)RootBase)
1>  
1>  Base::$vftable@RootBase@:
1>      | -16
1>   0  | &(vtordisp) Base::fas
1>  
1>  Base::fas this adjustor: 16
1>  Base::fa this adjustor: 0
1>  
1>  vbi    class     offset   o.vbptr  o.vbte fVtorDisp
1>         RootBase      16        4       4          1

Taking the exact same code, but making RootBase a regular base class (i.e., not virtual public RootBase, just public RootBase) has a significant effect on the object layout:

1>  class RootBase  size(8):
1>      +---
1>   0  | {vfptr}
1>   4  | ai
1>      +---
1>  
1>  RootBase::$vftable@:
1>      | &RootBase_meta
1>      |  0
1>   0  | &RootBase::fas
1>  
1>  RootBase::fas this adjustor: 0
1>  
1>  
1>  class Base  size(12):
1>      +---
1>      | +--- (base class RootBase)
1>   0  | | {vfptr}
1>   4  | | ai
1>      | +---
1>   8  | bai
1>      +---
1>  
1>  Base::$vftable@:
1>      | &Base_meta
1>      |  0
1>   0  | &Base::fas
1>   1  | &Base::fa
1>  
1>  Base::fas this adjustor: 0
1>  Base::fa this adjustor: 0

Note that with the virtual base gone, there is no longer a "who-am-i" offset stored, which appears to clean up the object format rather nicely.

Upvotes: 4

Related Questions