C++虚继承
什么是虚继承
子类在继承父类时,在声明前加virtual关键字,这就是虚继承
例:
class A { //...... } class B:virtual public A { //....... }
什么时候需要使用虚继承
在多重继承时,正常情况下子类对象将拥有继承链上所有父类对应的部分,但是如果两个父类都继承了同一个类,
那么在子类将拥有两份祖父类的数据,比如菱形继承:
下面我以四个简单类来分析其内存结构:
class CFuniture { int m_nFun; public: CFuniture() { m_nFun = 0; } virtual ~CFuniture() { } virtual void FunitureName() { cout << "CFuniture::FunitureName()" << endl; } }; class CSofa : public CFuniture { int m_nSofa; public: virtual void Sit() { cout << "CSofa::Sit()" << endl; } CSofa() { m_nSofa = 1; } virtual ~CSofa() { cout << "~CSofa()" << endl; } }; class CBed : public CFuniture { int m_nBed; public: CBed() { m_nBed = 2; } virtual void Sleep() { cout << "CBed::Sleep()" << endl; } virtual ~CBed() { } }; class CSofaBed :public CSofa, public CBed { int m_nSofaBed; public: CSofaBed() { m_nSofaBed = 3; } virtual ~CSofaBed() { cout << "~CSofaBed()" << endl; } virtual void SleepAndSit() { cout << "CSofaBed::SleepAndSit()" << endl; } virtual void SitAndSleep() { cout << "CSofaBed::SitAndSleep()" << endl; } virtual void Sleep() { cout << "CSofaBed::Sleep()" << endl; } virtual void Sit() { cout << "CSofaBed::Sit()" << endl; } }; int main(int argc, char **argv) { CSofaBed sofabed; return 0; }
这里家具类CFuniture祖父类,沙发类CSofa和床类CBed都继承继承自CFuniture,沙发床类CSofaBed同时继承
了CSofa和CBed,现在来看看CSofaBed对象的内存布局:
可以看出祖父类CFuniture在CSofaBed中有两份,这并非我们本意,我只想CSofaBed类对象中只有一份CFuniture
类数据,那么可以在CSofa和CBed继承CFuniture时,加上virtual关键字表示和其它类共享CFuniture:
class CSofa :virtual public CFuniture class CBed :virtual public CFuniture
此时再来看看CSofaBed对象的内存布局:
从监视窗口可以看出CFunitrue类的数据有独立的一部分,但是CSofa和CBed中依旧有CFunitrue类的数据,
其实这是VS的监视窗口为了更直观的显示继承体系,在CSofa和CBed中显示了CFunitrue类的数据,但是从
内存窗口中可以看出CFunitrue类的数据只有一份;
从上图可以看出CFunitrue类在CSofaBed子类中被独立出来,放在了对象的最尾部内存地址处,因为在CSofa和CBed以及CFunitrue中都有自己的虚函数,所以CSofaBed子类此时拥有三张属于自己虚表(和父类中的虚表不是同一个),另外CSofaBed子类自己新加的虚函数被追加到继承自CSofa虚表的后面,CSofaBed的析构函数被放到
继承自祖父类CFunitrue的虚表中
还可以从内存布局中看出CSofaBed的两个直接基类的虚表指针后面,还跟着两个指针:0x01259b68和0x01259bbc
这两个指针是偏移指针,它们两指向相同的结构体,该结构体共8字节,前四个字节无意义,后四个字节记录了偏移
指针距离CFunitrue类部分的字节数;从上图中可以看出sofbed对象中,CFunitrue类的部分位于0x0044FBDC
开始的,CSofa类部分的偏移指针在0x0044FBC0处,两者之间间隔了0x18个字节;CBed类部分的偏移指针位于0x0044FBD0处,和CFunitrue类的部分间隔了0x0C个字节