继承是C++做为OOD程序设计语言的三大特征(封装,继承,多态)之一,单一非多态继承是比较好理解的,本文主要讲解GCC环境下的多重继承和虚拟继承的对象内存布局。ios
1、多重继承函数
先看几个类的定义:布局
class Top { public: int a; }; class Left : public Top { public: int b; }; class Right : public Top { public: int c; }; class Bottom : public Left, public Right { public: int d; };
不难想象,Left和Right类的内存布局以下图所示:
咱们以下进行验证:spa
Left *left = new Left(); Top *top = left; cout << left << '\t' << top << endl;//输出:0x902c008 0x902c008 Right *right = new Right(); top = right; cout << right << '\t' << top << endl;//输出:0x902c018 0x902c018
从输出结果能够看出,父类指针top指向子类对象left和right的起始地址,与上述内存布局吻合。
在非虚拟多重继承的状况下,子类的内存布局是什么样子的呢?以下所示:设计
能够看出,Bottom类因为继承了Left和Right,而Left和Right又分别继承了Top。所以,Bottom包含了Top两次!指针
下面进行验证:code
Bottom *bottom = new Bottom();
// top = bottom; //error: ‘Top’ is an ambiguous base of ‘Bottom’
top = (Left *)bottom;
left = bottom; cout << bottom << '\t' << top << '\t' << left << endl;//输出:0x9930028 0x9930028 0x9930028 top = (Right *)bottom; right = bottom; cout << bottom << '\t' << top << '\t' << right << endl;//输出:0x9930028 0x9930030 0x9930030
好了,到这里讲完了非虚拟继承下的多重继承的内存布局状况,相信你们应该有一个比较清晰的认识了。最重要的一点是: 多重继承时,父类共同继承的祖父类会在子类中有多份存在。
2、虚拟继承对象
平时讨论的最多的是虚函数,不多涉及到虚拟继承的状况。那么,虚拟继承究竟是一个什么概念呢?继承
先来看一个例子: 内存
#include <iostream> using namespace std; class Father { public: int a; }; class Child : virtual public Father { public: int b; }; int main() { cout << sizeof(Father) << '\t' << sizeof(Child) << endl;//输出:4 12 Child child; cout << &child << '\t' << &child.b << '\t' << &child.a << endl;//输出:0xbfc08124 0xbfc08128 0xbfc0812c return 0; }
对,你没有看错,类的大小输出不是4 8,而是4 12。虚拟继承时,编译器会在子类中安插上一个虚表指针。
从输出的对象成员地址来看,咱们能够获得Child类的以下内存布局:
如今咱们对多重继承的例子进行改造:
class Top { public: int a; }; class Left : virtual public Top { public: int b; }; class Right : virtual public Top { public: int c; }; class Bottom : public Left, public Right { public: int d; };
把Left和Right改为了虚拟继承Top。
从上面验证简单虚拟继承时,编译器安插虚表指针的例子,咱们能够想象出此时Bottom类的对象内存布局以下:
对,你没有看错!虚拟继承时,子类只有父类共同继承的祖父类的一份存在。这其实也就是虚拟继承的最大用途。此时,Top,Left,Right和Bottom对象的大小分别为:4 ,12 ,12 ,24。
既然有虚表指针了,那么Bottom的虚表是什么样的呢?请看:
有了虚表,内存布局状况一目了然。下面咱们进行验证:
Bottom *bottom = new Bottom(); top = bottom; cout << bottom << '\t' << top << endl;//输出:0x9fa5028 0x9fa503c Left *left = bottom; cout << bottom << '\t' << left << endl;//输出:0x9fa5028 0x9fa5028 Right *right = bottom; cout << bottom << '\t' << right << endl;//输出:0x9fa5028 0x9fa5030
根据输出结果,咱们能够知道指针的指向状况:
因为引入了虚指针和虚表,left指针和right指针能够根据虚表提供的偏移量信息,轻松访问到Top::a。
到此为止,已经讨论清楚了多重继承和虚拟继承下的对象内存布局状况。总结下:非虚拟多重继承时,子类会有父类
共同继承祖父类的多份存在;虚拟继承时,子类会被安插一个虚拟指针;多重虚拟继承时,子类只有父类共同继承祖父类的一
份存在。经过父类的虚拟指针,能够正确地访问祖父类中的成员。
参考文献:
1.http://www.tbdata.org/archives/878
2. 《深度探索C++对象模型》