c++类实例对象内存布局

来源:互联网 发布:java 递归 编辑:程序博客网 时间:2024/06/05 11:49

首先为cpp文件添加额外的命令行选项 /d1 reportAllClassLayout
下面对几种情况分别讨论

单一简单类

class father{public:    father(){};    ~father(){};public:    void doSomething(){};    static void sDoSomething(){};private:    char a;    int b;    static int c;};

对他进行编译,我们可以在控制台看到

1> class father size(8):
1> + - - -
1> 0 | a
1>    | < alignment member >(size=3)
1> 4 | b
1> + - - -

开头的0,4为类内偏移。我们可以看到静态变量、函数和静态函数都不占类实例对象的内存空间。且中间有一段空白区域因内存对齐而空出来。

带有虚函数的单一类

将上面的代码简化并添加虚函数

class father{public:    father(){};    virtual ~father(){};private:    char a;    int b;public:    virtual void doSomething(){};};

编译结果如下

1> class father size(12):
1> + - - -
1> 0 | {vfptr}
1> 4 | a
1>    | < alignment member > (size=3)
1> 8 |  b
1> + - - -
1>
1> father::$vftable@:
1>    | &father_meta
1>    | 0
1> 0 | &father::{dtor}
1> 1 | &father::doSomething

我们可以看到跟没有虚函数相比,类的开头多了一个{vfptr},这个就是虚函数表指针,在32位下占用4字节。
继续往下看,在&father_meta下方的0表示这个虚函数表指针在类内的偏移,不过在vs2013中查看内存并没有在虚函数表的开头看到这个偏移量所占的内存。
虚函数表的开头数字则只是表示序号而不是偏移。

无虚函数的多继承

class father{private:    char a;    int b;public:    void doSomething(){};};class mother{private:    char a;    int b;public:    void doSomething(){};};class son :public father,public mother{public:    char a;    int b;public:    void doSomething(){};};

编译结果如下,只把son类结构贴出来

1> class son size(24):
1>  + - - -
1>    | + - - - (base class father)
1> 0 | | a
1>    | | < alignment member > (size=3)
1> 4 | | b
1>    | + - - -
1>    | + - - - (base class mother)
1> 8 | | a
1>    | | < alignment member > (size=3)
1> 12  | | b
1>    | + - - -
1> 16  | a
1>    | < alignment member > (size=3)
1> 20  | b
1>  + - - -

通过观察发现无虚函数的正常继承,在内存中按继承的从左往右顺序连续排列,最后才是派生类。

有虚函数的多继承

class father{private:    char a;    int b;public:    virtual void doSomething(){};};class mother{private:    char a;    int b;public:    virtual void doSomething(){};};class son :public father,public mother{private:    char a;    int b;public:    virtual void doSomething(){};    virtual void sonDoHimself(){};};

编译结果如下

1>  class son   size(32):1>      +---1>      | +--- (base class father)1>   0  | | {vfptr}1>   4  | | a1>      | | <alignment member> (size=3)1>   8  | | b1>      | +---1>      | +--- (base class mother)1>  12  | | {vfptr}1>  16  | | a1>      | | <alignment member> (size=3)1>  20  | | b1>      | +---1>  24  | a1>      | <alignment member> (size=3)1>  28  | b1>      +---1>  1>  son::$vftable@father@:1>        | &son_meta1>        |  01>   0  | &son::doSomething1>   1  | &son::sonDoHimself1>  1>  son::$vftable@mother@:1>        | -121>   0    | &thunk: this-=12; goto son::doSomething

通过观察发现,本来只在son类存在的虚函数sonDoHimself的指针存在于father类的虚函数表的末尾。mothe和father类的虚函数表中的doSomething函数也都指向了son类,这也是实现多态的关键。

菱形继承

class Grandfather{private:    char a;    int b;public:    virtual void doSomething(){};};class father:public Grandfather{private:    char a;    int b;public:    virtual void doSomething(){};};class mother :public Grandfather{private:    char a;    int b;public:    virtual void doSomething(){};};class son :public father,public mother{private:    char a;    int b;public:    virtual void doSomething(){};    virtual void sonDoHimself(){};};

编译结果如下

1>  class son   size(48):1>      +---1>      | +--- (base class father)1>      | | +--- (base class Grandfather)1>   0  | | | {vfptr}1>   4  | | | a1>      | | | <alignment member> (size=3)1>   8  | | | b1>      | | +---1>  12  | | a1>      | | <alignment member> (size=3)1>  16  | | b1>      | +---1>      | +--- (base class mother)1>      | | +--- (base class Grandfather)1>  20  | | | {vfptr}1>  24  | | | a1>      | | | <alignment member> (size=3)1>  28  | | | b1>      | | +---1>  32  | | a1>      | | <alignment member> (size=3)1>  36  | | b1>      | +---1>  40  | a1>      | <alignment member> (size=3)1>  44  | b1>      +---1>  1>  son::$vftable@father@:1>      | &son_meta1>      |  01>   0  | &son::doSomething1>   1  | &son::sonDoHimself1>  1>  son::$vftable@mother@:1>      | -201>   0  | &thunk: this-=20; goto son::doSomething

通过观察发现,Grandfather类竟然分别在father和mother类存在一份。

虚继承

直接来看菱形的虚继承

class Grandfather{private:    char a;    int b;public:    virtual void doSomething(){};};class father :virtual public Grandfather{private:    char a;    int b;public:    virtual void doSomething(){};};class mother :virtual public Grandfather{private:    char a;    int b;public:    virtual void doSomething(){};    virtual void motherDoHerself(){};};class son :public father,public mother{private:    char a;    int b;public:    virtual void doSomething(){};    virtual void sonDoHimself(){};};

编译结果如下

1>  class father    size(24):1>      +---1>   0  | {vbptr}1>   4  | a1>      | <alignment member> (size=3)1>   8  | b1>      +---1>      +--- (virtual base Grandfather)1>  12  | {vfptr}1>  16  | a1>      | <alignment member> (size=3)1>  20  | b1>      +---1>  1>  father::$vbtable@:1>   0  | 01>   1  | 12 (fatherd(father+0)Grandfather)1>  1>  father::$vftable@:1>      | -121>   0  | &father::doSomething1>  1>  father::doSomething this adjustor: 121>  1>  vbi:       class  offset o.vbptr  o.vbte fVtorDisp1>       Grandfather      12       0       4 01>  1>  1>  class mother    size(28):1>      +---1>   0  | {vfptr}1>   4  | {vbptr}1>   8  | a1>      | <alignment member> (size=3)1>  12  | b1>      +---1>      +--- (virtual base Grandfather)1>  16  | {vfptr}1>  20  | a1>      | <alignment member> (size=3)1>  24  | b1>      +---1>  1>  mother::$vftable@mother@:1>        | &mother_meta1>        |  01>   0  | &mother::bbb1>  1>  mother::$vbtable@:1>   0  | -41>   1  | 12 (motherd(mother+4)Grandfather)1>  1>  mother::$vftable@Grandfather@:1>      | -161>   0  | &mother::doSomething1>  1>  mother::doSomething this adjustor: 161>  mother::bbb this adjustor: 01>  1>  vbi:       class  offset o.vbptr  o.vbte fVtorDisp1>       Grandfather      16       4       4 01>  1>  1>  class son   size(48):1>      +---1>      | +--- (base class mother)1>   0  | | {vfptr}1>   4  | | {vbptr}1>   8  | | a1>      | | <alignment member> (size=3)1>  12  | | b1>      | +---1>      | +--- (base class father)1>  16  | | {vbptr}1>  20  | | a1>      | | <alignment member> (size=3)1>  24  | | b1>      | +---1>  28  | a1>      | <alignment member> (size=3)1>  32  | b1>      +---1>      +--- (virtual base Grandfather)1>  36  | {vfptr}1>  40  | a1>      | <alignment member> (size=3)1>  44  | b1>      +---1>  1>  son::$vftable@mother@:1>      | &son_meta1>      |  01>   0  | &mother::bbb1>   1  | &son::sonDoHimself1>  1>  son::$vbtable@father@:1>   0  | 01>   1  | 20 (sond(father+0)Grandfather)1>  1>  son::$vbtable@mother@:1>   0  | -41>   1  | 32 (sond(mother+4)Grandfather)1>  1>  son::$vftable@:1>      | -361>   0  | &son::doSomething1>  1>  son::doSomething this adjustor: 361>  son::sonDoHimself this adjustor: 01>  1>  vbi:       class  offset o.vbptr  o.vbte fVtorDisp1>     Grandfather      36      16       4 0

首先观察father类发现它的开头变成了{vbptr},这个就是虚基类指针,它所指的内容第一个为自身类内偏移,其后依次为从左往右的虚继承父类在类内的偏移。观察mother类发现,如果虚继承后派生类有自己的虚函数,会另生成一个虚函数表,位于本类的开头,虚基类指针总是位于虚表指针之后。观察son类发现一个有趣的现象,mother类排在了father前面,经试验,应该先是有虚表的排前面,然后再按从左往右的顺序往下排。虚继承就是通过使各个类持有一个虚基类指针而保证共同继承的父类只有一份。

总结

c++当存在虚函数和虚继承的时候会有额外的空间成本和执行期的效率降低(同c比)。