您的位置:首页 > 其它

继承(五)

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看下地址便知:



关于这节比较绕,需细细体会~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: