在前一篇,咱们讨论了在多继承情况下,具备虚函数的类的内存布局状况。本篇将进一步探索在多重继承,即具备重复继承的状况下的内存布局状况。在阅读本篇和下一篇以前,建议先阅读本博客另外一篇博文《浅析GCC下C++多重继承 & 虚拟继承的对象内存布局》。ios
先说一点题外话,细心的读者可能会发现,咱们在探索不一样状况下类的内存布局时,老是先经过查看类的大小以及其中各个成员变量的地址来进行分析,而后再具体定位某一位置的值。从最原始的内存中的对象分布,咱们能够得到最深刻最有效的理解。函数
OK,请看例子:布局
#include <iostream> using namespace std; class Top { public: virtual void x(){cout << "top x" << endl;} virtual void print0(){cout << "top print" << endl;} public: int a; }; class Left:public Top { public: virtual void y(){cout << "left y" << endl;} virtual void print1(){cout << "left print" << endl;} public: int b; }; class Right:public Top { public: virtual void z(){cout << "right z" << endl;} virtual void print2(){cout << "right print" << endl;} public: int c; }; class Bottom : public Left, public Right { public: virtual void y(){cout << "bottom y" << endl;} virtual void z(){cout << "bottom z" << endl;} virtual void print3(){cout << "bottom print" << endl;} public: int d; }; int main() { /*first part*/ cout << sizeof(Top) << "\t" << sizeof(Left) << "\t" << sizeof(Right) << "\t" << sizeof(Bottom) << endl; //输出:8 12 12 28 Bottom *b = new Bottom(); cout << b << " " << &b->Left::a << " " << &b->b << " " << &b->Right::a << " " << &b->c << " " << &b->d << endl; //输出:0x8c0f008 0x8c0f00c 0x8c0f010 0x8c0f018 0x8c0f01c 0x8c0f020 /*second part*/ typedef void (*Func)(void); Func pFunc; pFunc = (Func)*((int *)*(int *)(b)); pFunc();//输出:top x pFunc = (Func)*((int *)*(int *)(b)+1); pFunc();//输出:top print pFunc = (Func)*((int *)*(int *)(b)+2); pFunc();//输出:bottom y pFunc = (Func)*((int *)*(int *)(b)+3); pFunc();//输出:left print pFunc = (Func)*((int *)*(int *)(b)+4); pFunc();//输出:bottom z pFunc = (Func)*((int *)*(int *)(b)+5); pFunc();//输出:bottom print //pFunc = (Func)*((int *)*(int *)(b)+6); //pFunc();//段错误 /*third part*/ pFunc = (Func)*((int *)*((int *)(b)+3)); pFunc();//输出:top x pFunc = (Func)*((int *)*((int *)(b)+3)+1); pFunc();//输出:top print pFunc = (Func)*((int *)*((int *)(b)+3)+2); pFunc();//输出:bottom z pFunc = (Func)*((int *)*((int *)(b)+3)+3); pFunc();//输出:right print //pFunc = (Func)*((int *)*((int *)(b)+3)+4); //pFunc();//段错误 delete b; return 0; }
对于上面的例子,咱们分为三部分进行讲解。
第一部分:多重继承状况下,对象自己(除虚函数表外)的内存布局。spa
从代码中first part的输出状况来看,Top、Left和Right的大小很容易理解,至于Bottom类的大小,若是你看过参考文献一,那么也很容易理解。Bottom类包含了两次Top类中的a成员变量,所以总共有5个int成员变量,为20字节。再加上两个虚指针,即为28字节。.net
从输出的Bottom对象中成员变量地址状况,咱们能够用以下图来表示内存布局:指针
简单地说,多重继承时,子类会有多个祖父类的存在。code
第二部分:多重继承状况下,主要虚函数表的内存布局。orm
从代码的second part的输出状况来看,咱们能够用下图来表示主要虚函数表的内存布局:对象
第三部分:多重继承状况下,次要虚函数表的内存布局。blog
从代码的third part的输出状况来看,咱们能够用下图来表示次要虚函数表的内存布局:
对比前一篇多继承状况下类的内存布局,咱们能够发现:在多重继承状况下,不只祖父类的成员变量在子类中会有多份存在,祖父类的虚函数一样会在子类的虚函数表中有多份存在,分别位于主要虚函数表和次要虚函数表中。
参考文献:
1.《浅析GCC下C++多重继承 & 虚拟继承的对象内存布局》
2. 《深度探索C++对象模型》