关键词:
C++、虚拟继承、虚基类
摘 要:
C++支持多重继承,但多重继承带来了继承的模糊性问题 。C++的虚拟继承在多重继承时防止
二义性,解决由多重继承带来的模糊性问题。
先来看看多重继承带来的模糊性问题:
继承关系图:
运行上面的代码(VC6.0)会出现编译错误:error C2385: 'CChildC::m_nParent' is ambiguous。编译器会提示'CChildC::m_nParent是模糊不清的,因为在这种继承的情况,CChildC::m_nParent会有2份拷贝,分别从类CChildA和CChildB继承而来。
解决案例描述的问题有两种方法:
方法一:是在被调用的成员变量前面加上类限定符号,明确表明在成员变量是那个基继承而
的。例如:cChildC.CChildA::m_nParent(指明是从类CChildA中继承来的)或:cChildC.CChildB::m_nParent(指明是从类CChildB中继承来的)。
方法二:就是利用C++虚拟继承特性。虚拟继承:在继承定义中包含了virtual关键字的继承关系; 虚基类:在虚继承体系中的通过virtual继承而来的基类。将程序的CChildA和CChildB继承CParent是改为虚拟继承:class CChildA: virtual public CParent和class CChildB: virtual public CParent。
下面是改为虚拟继承后的代码:
上面代码运行结果:
可以看到cChildC.m_nParent的结果为3,继承自CChildB。关于cChildC.m_nParent到底继承来自那个类,跟CChildC继承时候的基类的书写顺序有关。本例为class CChildC: public CChildA, public CChildB。但如果写成class CChildC: public CChildB, public CChildA的话,那么结果将是2。
如果我们打印上面各个类的大小,在非虚拟继承情况下:
在虚拟继承的情况:
明显可以看出他们的类大小是不一样的。可以分析一下为什么会出现在这种情况。
首先,来看下在非虚拟继承的情况下:CParent只有一个int型 m_nParent成员占4个字节,
CChildA有从CParent 继承来m_nParent成员和自己的一个int型新成员m_nChildB,一共占8个字节,同样CChildB也占8个字节。CChildC继承了CChildA和CChildB的成员,有16个字节,再加上自己的新添加的的int型成员,所以有20个字节。
非虚拟继承的情况下,内存的分配地址:
上图可以直观的看到,在非虚拟继承下,类对象的内存布局。继承的成员变量在前,非继承的成员变量在后。在继承多个基类的情况下,内存布局原则是,从左到右的顺序,例如:class CChildC: public CChildA, public CChildB,那么内存布局就是继承自CChildA的成员变量在前。
再来看虚拟继承的情况:CParent同上仍然是4个字节。 CChildA虚拟继承CParent,编译器会在它的对象的首地址的位置,添加一个虚基类表指针vbptr指向派生类(CChildA)的virtual bass class table(虚基类表),虚基类表中存放的是派生类的虚基类表指针到虚基类实例指针的偏移量。位于虚基类表的第二项,最后一项为0,意味着虚基类表的结束。那么CChildA就是12个字节(1个vbptr + 1个继承自虚基类的int型变量m_nParent + 1个自己新增加的int型变量m_nChildA)了。同样的道理,CChildB同样是12个字节。
下图描述了CChildA对象的内存布局和他的vbptr ,不难发现,虚基类实例地址 = vbptr + offset。
CChildA对象内存布局:
CChildC对象的内存布局比上面的要复杂一些,CChildA和CChildB同时被CChildC普通继承(非虚继承),根据C++标准的要求,基类在在派生类中保证其原始的完整性,因此两个vbptr被继承到了CChildC类;由于CParent被虚继承,可以看到CParent m_nParent只有一份拷贝,而且放在最后。
CChildC的大小应该是:2个vbptr + 1个继承自CChildA的m_nChildA + 1个继承自CChildB的m_nChildB + 1份自己新增的m_nChildC + 1份CParent m_nParent拷贝 。所以一共有24个字节。
CChildC对象内存布局:
C++虚拟继承机制在各个编译器下是有所不同的,本文的讲述都是在VC6.0的环境下为前提的。也只是讲解了一下虚拟继承的一种情况。一般情况应该慎用虚拟继承,因为这样只会降低效率和占用更多的空间。