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

本文深入探讨了C++类的内存布局,包括普通类、含有继承、虚函数、虚拟继承以及菱形继承的情况。通过VS2015调试,展示了虚表、成员变量的分布以及内存调整。总结了虚继承如何减少基类重复,但增加虚表指针负担。
摘要由CSDN通过智能技术生成

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

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

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


  • 部署环境

    我们在 属性->C/C++ ->命令行 -> /d1 reportSingleClassLayoutXXX ,XXX表示类名;

在这里插入图片描述


  • 单个基础类

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

    内存分布:

    class Base size(8):
      	+-- -
       0 | a
       4 | b
      	+-- -
    

    总结:我们发现普通类的内存分布是根据声明的顺序进行的,成员函数不占用内存。


  • 基础类+继承类

    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 Base	size(12) :
      	+-- -
       0 | {vfptr}
       4 | a
       8 | b
      	+-- -
    
      Base::$vftable@:
     | &Base_meta
     | 0
       0 | &Base::run
    

    总结:带有虚函数的内存分布分为两部分,一部分是内存分布,一部分是虚表;我们从最上面发现,vfptr是放在了内存开始处,然后才是成员变量;虚函数run前面表示这个虚函数的序号为0


  • 含有虚函数的基类+继承子类

    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;所以说:虚继承的作用是减少了对基类的重复,代价是增加了虚表指针的负担(增加了更多的需指针)


ase 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


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

**********

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值