一起探索C++类内存分布
Posted C语言与CPP编程
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一起探索C++类内存分布相关的知识,希望对你有一定的参考价值。
一起探索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
总结:通过内存分布可知,
Divide1
和Divide2
都是两个虚表,Divide中却是成了3个虚表,只有一份base;所以说:虚继承的作用是减少了对基类的重复,代价是增加了虚表指针的负担(增加了更多的需指针)
以上是关于一起探索C++类内存分布的主要内容,如果未能解决你的问题,请参考以下文章