专栏名称: CPP开发者
伯乐在线旗下账号,「CPP开发者」专注分享 C/C++ 开发相关的技术文章和工具资源。
目录
51好读  ›  专栏  ›  CPP开发者

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

CPP开发者  · 公众号  ·  · 2021-04-27 12:10

正文

一起探索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






请到「今天看啥」查看全文