C++学习之路: 虚继承的内存的模型
写给出结论:
1.类大小计算遵循结构体对其原则
2.类的大小与数据成员有关,与成员函数无关
3.类的大小与静态数据成员无关
4. 虚继承对类的大小影响
5.虚函数对类的大小影响
研究背景)
我们以这个钻石型继承来研究下,虚继承后派生类DD的内存分布,先说原理和推测,来理解编译器的处理是如何在内存上体现的。
保持我们一贯的做风。
#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) { cout<<sizeof(BB)<<endl; cout<<sizeof(B1)<<endl; cout<<sizeof(DD)<<endl; B1 b1; long** p; cout<<&b1<<endl; cout<<&b1.bb_<<endl; cout<<&b1.b1_<<endl; p = (long**)&b1; cout<<p[0][0]<<endl; cout<<p[0][1]<<endl; DD dd; cout<<&dd<<endl; cout<<&dd.bb_<<endl; cout<<&dd.b1_<<endl; cout<<&dd.b2_<<endl; cout<<&dd.dd_<<endl; p = (long**)ⅆ cout<<p[0][0]<<endl; cout<<p[0][1]<<endl; cout<<endl; cout<<p[2][0]<<endl; cout<<p[2][1]<<endl; BB* pp; pp = ⅆ pp->bb_; // ͨ¹ý¼ä½Ó·ÃÎÊ£¬ÕâÐèÒªÔËÐÐʱµÄÖ§³Ö return 0; }
结果打印
4 12 24 0xbfacd81c 0xbfacd824 0xbfacd820 0 134515772 0xbfacd804 0xbfacd818 0xbfacd808 0xbfacd810 0xbfacd814 12 -8 134515700 134515740
根据结果,我们可以把几个类的内存分布画出来
@B1的内存分布,可以通过照相记忆法。
有以下特点
1。 一般来说编译器都会把虚继承的基类放置在内存底部,和构造顺序是一致的,在小端机上是
高地址存放低字节,所以先构造bb_基类放置在底部。然后构造自身是B1
空白处世虚基类表指针,也占据4个字节
vbptr虚基类指针指向一个虚基类表,这个表记录着两个偏移量,
已在图上标注,*(指针和本地址的差) *(指针和虚基类的差),大家自己减一下就可以算出偏移量分布式0和8;
@DD对象:多虚基类继承下的内存分布
可以看的出内存分布和和继承,与构造顺序的关系,基类总是先被构造,继承离最远派生类越远,越先构造,所以看到,基类BB最先被构造,然后是B2,最后是B1。
当B1构造结束,那么整个DD类便构造完成了
结论:通过派生类指针访问虚基类数据成员,需要在运行时动态的找到偏移地址,
数据间接访问。