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比)。
- c++类实例对象内存布局
- 类对象 内存布局
- C字节对齐与C++类对象内存布局
- C++-对象继承中的内存布局
- C++类对象内存布局
- C中内存堆栈的布局---linux 简单实例
- [C++]VS2008使用命令行选项查看对象的内存布局
- [C++]VS2008使用命令行选项查看对象的内存布局
- 类的内存布局-sizeof(类对象)
- C++派生类对象内存布局
- 普通类对象的内存布局
- C++类、结构对象内存布局浅析
- MSVC查看类的对象内存布局
- C++类、结构对象内存布局浅析
- C++内存对象布局
- c++对象,内存布局
- c++对象内存布局
- 对象内存布局 (1)
- 使用php解决魔术师发牌问题
- 1218: n个人的工作分配
- hibernate中语句貌似不执行的问题
- 一周入门Kotlin(五)
- 一些常见的树结构
- c++类实例对象内存布局
- 项目学习类容,日期处理,SQL语句查询,hashtable的运用,R语言包的环境配置
- C/C++_log2000_空白窗口3及初步gui编程笔记
- 每天学一点Swift----面向对象上(十二)
- Django07-数据库接口QuerySet API
- Linux编程学习之信号篇-pause与sigsuspend
- Windows进程管理
- 写给后端程序员的 HTTP 缓存原理介绍
- shell脚本中cd命令无效的解决方案