继承(五)
2016-06-27 22:44
169 查看
在研究之前,先来回忆一下类/对象大小的计算:
类大小计算遵循前面学过的结构体对齐原则。
类的大小与数据成员有关与成员函数无关。
类的大小与静态数据成员无关。
虚继承对类的大小的影响
虚函数对类的大小的影响
前面三点在之前已经介绍过了,这节主要是研究第四点,而第五点会在未来进行探讨,在探讨虚继承对内存模型的影响时,需要了解一下“虚基类表”:
virtual base table
本类地址与虚基类表指针地址的差。
虚基类地址与虚基类表指针地址的差。
virtual base table pointer(vbptr)
下面先依照上面的类图的结构来构建数据模型,来细细探讨:
#include <iostream> using namespace std; class BB { public: int bb_; }; class B1 : virtual public BB { public: int b1_; }; class B2 : virtual public BB { public: int b2_; }; class DD : public B1, public B2 { public: int dd_; }; int main(void) { return 0; }
这里数据成员都是整型,就不会受结构体对齐的影响,比较容易算出它的大小。下面分别来打印一下各个类的大小:
编译运行:
可见由于有了虚继承,则并非按我们的预想来计算类大小了,下面来推导一下这种情况的内存模型B1和DD,首先来推导一下B1类的内存模型:
编译运行:
打印出地址之后,下面来进行推导:
根据vbtl的存放规则来计算一下它里面的值是多少?
这样B1的内存模型就推导出来了,口说无评,下面用代码主要来论证上面画的内存模型中的vbtl存放的是对的:
编译运行:
刚好论证了之前推断的模型是正确的,所以可以看到B1类的大小为12个字节,因为第一个字节是空出来存放虚基类表指针的。
用这种方法再来推断一下DD类的内存模型:
编译运行:
打印地址之后下面来推导:
那先来计算填充一下虚基类表中的值:
下面用代码来验证一下:
编译运行:
论证了结果是正确的,目前还未学习到虚函数,如果有了它内存模型会更加复杂,因为虚函数会有虚表指针,它指向虚表,这个在之后再来探讨。
想一下为什么有了虚继承内存模型会变得如此复杂?
先从"virtual"这个单词的意思来理解:它是存在的、共享的、间接的,拿DD这个派生类来说,虚基类BB是存在的;对DD数据成员来说是共享的,BB不会创建两份;对于DD对象要访问BB数据成员需要间接访问,其间接访问就体现在虚基类表指针了,如下:
对于DD来说,如果要找到BB,需要通过B1或B2的虚基类表的偏移位置最终找到BB:
说到这,抛出一个问题:dd对象要访问bb这个数据成员,是直接访问还是间接访问呢?
实际上如果是对象访问的话还是直接访问,因为内存模型在编译时刻已经决定了,但是,如果通过指针来访问情况就不一样了:
为什么说是间接访问呢?DEBUG看下地址便知:
关于这节比较绕,需细细体会~
相关文章推荐
- 师大+演示文档
- php curl向远程服务器上传文件
- WPF控件大小变化导致闪烁的可能原因
- centos 安装 codeblocks
- struts2 action 3中书写方式
- XCode 7.2 真机调试 ios9.3系统遇到的 "could not find developer disk image "
- (?)企业部分之MySQl主从复制
- 分布式之RPC的协议以及错误处理
- BZOJ 1179: [Apio2009]Atm
- cookie
- 安装完ubuntu后,启动时没有windows选项
- 何为“超精简”(超晶简)版软件?何为“懂你版”?
- Power of Four
- struts 2.5.1第一个测试应用
- ios 多线程 -- NSOperation 常用方法
- PowerDesigner使用教程(一)
- ssh配置事务
- CodeForces 444C DZY Loves Colors题解
- hdu_5274_Dylans loves tree(树剖)
- 蓝牙 BCSP 解析 源码分析