一起探索C++类内存分布

254次阅读  |  发布于2年以前

一起探索C++类内存分布

C++ 类中内存分布具体是怎么样,尤其是C++中含有继承、虚函数、虚拟继承以及菱形继承等等情况下。

由于在linux下没有windows下显示直观,我们采用vs2015进行调试。



  class Base
{
 int a;
 int b;
public:
 void test();
};

class Divide :public Base
{
public:
 void run();
private:
 int c;
 int d;
};

内存分布:

  class Divide size(16) :
   +-- -
   0 | +-- - (base class Base)
   0 | | a
   4 | | b
 | +-- -
   8 | c
  12 | d
   +-- -

总结:根据内存分布,我们发现普通继承类,内存分布也是按照声明的顺序进行的,成员函数不占用内存;类的顺序是先基类,后子类。


  class Base
{
 int a;
 int b;
public:
 void test();
 virtual void run();
};

class Divide :public Base
{
public:
 void DivideFun();
 virtual void run();
private:
 int c;
 int d;
};

内存分布:

  class Divide size(20) :
   +-- -
   0 | +-- - (base class Base)
   0 | | {vfptr}
   4 | | a
   8 | | b
 | +-- -
  12 | c
  16 | d
   +-- -

  Divide::$vftable@:
 | &Divide_meta
 | 0
   0 | &Divide::run

总结:我们发现继承类,虚表只有一个,还是在内存开始处,内存排布顺序与普通继承类是一致的;


  class Base
{
 int a;
 int b;
public:
 void test();
 virtual void run();
};

class Divide :public Base
{
public:
 void DivideFun();
 virtual void run();
 virtual void DivideRun();
private:
 int c;
 int d;
};

内存分布:

  class Divide size(20) :
   +-- -
   0 | +-- - (base class Base)
   0 | | {vfptr}
   4 | | a
   8 | | b
 | +-- -
  12 | c
  16 | d
   +-- -

  Divide::$vftable@:
 | &Divide_meta
 | 0
   0 | &Divide::run
   1 | &Divide::DivideRun

总结:虚表还是继承于基类,在虚表部分多了DivideRun序号为1的虚函数;


  class Base
{
 int a;
 int b;
public:
 virtual void run();
};

class Divide1 :public Base
{
public:
 virtual void run();
private:
 int c;
};

class Divide2 :public Base
{
public:
 virtual void run();
private:
 int d;
};

class Divide :public Divide1, Divide2
{
public:
 virtual void run();
private:
 int d;
};

内存分布:

  class Divide1 size(16) :
   +-- -
   0 | +-- - (base class Base)
   0 | | {vfptr}
 4 | | a
 8 | | b
| +-- -
 12 | c
  +-- -

 Divide1::$vftable@:
| &Divide1_meta
| 0
  0 | &Divide1::run

 Divide1::run this adjustor: 0

 class Divide2 size(16) :
  +-- -
   0 | +-- - (base class Base)
   0 | | {vfptr}
  4 | | a
  8 | | b
| +-- -
 12 | d
  +-- -

 Divide2::$vftable@:
| &Divide2_meta
| 0
  0 | &Divide2::run

 Divide2::run this adjustor: 0

 class Divide size(36) :
  +-- -
   0 | +-- - (base class Divide1)
   0 | | +-- - (base class Base)
   0 | | | {vfptr}
 4 | | | a
 8 | | | b
| | +-- -
 12 | | c
| +-- -
  | +-- - (base class Divide2)
  | | +-- - (base class Base)
  | | | {vfptr}
  | | | a
  | | | b
| | +-- -
  | | d
| +-- -
  | d
  +-- -

 Divide::$vftable@Divide1@:
| &Divide_meta
| 0
  0 | &Divide::run

 Divide::$vftable@Divide2@:
| -16
  0 | &thunk: this -= 16; goto Divide::run

 Divide::run this adjustor: 0

总结:主要看最后一个Divide类,内存排列顺序先是Divide1,后是Divide2,在Divide1和Divide2中各有一份虚表;


  class Base
{
 int a;
 int b;
public:
 virtual void run();
};

class Divide1 :virtual public Base
{
public:
 virtual void run();
private:
 int c;
};

class Divide2 :virtual public Base
{
public:
 virtual void run();
private:
 int d;
};

class Divide :public Divide1, Divide2
{
public:
 virtual void run();
private:
 int d;
};

内存分布:

  class Divide1 size(20) :
    +-- -
    0 | {vbptr}
   4 | c
   +-- -
   +-- - (virtual base Base)
   8 | {vfptr}
  12 | a
  16 | b
   +-- -

  Divide1::$vbtable@:
   0 | 0
   1 | 8 (Divide1d(Divide1 + 0)Base)

  Divide1::$vftable@:
 | -8
   0 | &Divide1::run

  Divide1::run this adjustor: 8
  vbi:    class  offset o.vbptr  o.vbte fVtorDisp
              Base       8       0       4 0

  class Divide2 size(20) :
   +-- -
   0 | {vbptr}
   4 | d
   +-- -
   +-- - (virtual base Base)
   8 | {vfptr}
  12 | a
  16 | b
   +-- -

  Divide2::$vbtable@:
   0 | 0
   1 | 8 (Divide2d(Divide2 + 0)Base)

  Divide2::$vftable@:
 | -8
   0 | &Divide2::run

  Divide2::run this adjustor: 8
  vbi:    class  offset o.vbptr  o.vbte fVtorDisp
              Base       8       0       4 0

  class Divide size(32) :
   +-- -
   0 | +-- - (base class Divide1)
   0 | | {vbptr}
   4 | | c
 | +-- -
   8 | +-- - (base class Divide2)
   8 | | {vbptr}
  12 | | d
 | +-- -
  16 | d
   +-- -
   +-- - (virtual base Base)
  20 | {vfptr}
  24 | a
  28 | b
   +-- -

  Divide::$vbtable@Divide1@:
   0 | 0
   1 | 20 (Divided(Divide1 + 0)Base)

  Divide::$vbtable@Divide2@:
   0 | 0
   1 | 12 (Divided(Divide2 + 0)Base)

  Divide::$vftable@:
 | -20
   0 | &Divide::run

总结:通过内存分布可知,Divide1Divide2都是两个虚表,Divide中却是成了3个虚表,只有一份base;所以说:虚继承的作用是减少了对基类的重复,代价是增加了虚表指针的负担(增加了更多的需指针)

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8