若D1继承自B,D2继承自B,D继承自D1和D2,默认情况下派生类将含有继承链上每个类对应的子部分。如果某个类在派生过程中出现了多次,则派生类中将包含该类的多个子对象。
虚继承解决了这一问题,虚继承的目的是令某个类做出声明,承诺愿意共享它的基类。其中,共享的基类子对象成为虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含唯一一个共享的虚基类子对象。
我们指定虚基类的方式是在派生列表中添加关键字virtual,virtual说明符表明了一种愿望,即在后续的派生类当中共享虚基类的同一份实例。
如图
Panda通过Raccoon和Bear继承了ZooAnimal,因为Raccoon和Bear继承ZooAnimal的方式都是虚继承,所以在Panda中只有一个ZooAnimal基类部分。
如何初始化
在虚派生中,虚基类是由最低成的派生类初始化的。以上图为例,当创建Panda对象时,由Panda的构造函数独自控制ZooAnimal的初始化过程。
为了理解这一规则,我们不妨假设当以普通规则处理初始化任务时会发生什么情况。在此例中,虚基类将会在多条继承路径上被重复初始化。以ZooAnimal为例,如果应用普通规则,则Raccoon和Bear都会试图初始化Panda对象的ZooAnimal部分。
当然继承体系中的每个类都可能在某个时刻成为“最低层的派生类”。故只要我们能创建虚基类的派生类对象,则该派生类的构造函数就必须初始化它的虚基类。
构造顺序问题
虚基类总是先于非虚基类构造,与它们在继承体系中的次序和位置无关。
一个类可以有多个虚基类。此时,这些虚基类的子对象按照它们在派生列表中出现的顺序从左到右依次构造。例如:
class Character {/*...*/};
class BookCharacter : public Character {/*...*/};
class ToyAnimal {/*...*/};
class TeddyBear : public BookCharacter, public Bear, virtual public ToyAnimal
{/*...*/};
编译器按照直接基类的声明顺序对其依次进行检查,以确定其中是否含有虚基类。如果有,则先构造虚基类,然后按照声明的顺序逐一构造其它非虚基类。因此,要想创建一个TeddyBear对象,需要按照如下次序调用这些构造函数:
ZooAnimal(); //Bear的虚基类
ToyAnimal(); //直接虚基类
Character(); //第一个非虚基类的间接基类
BookCharacter(); //第一个直接非虚基类
Bear(); //第二个直接非虚基类
TeddyBear(); //最低层派生类
再如:
#include "iostream.h" class base1 { public: base1(){cout<<"class base1"<<endl;} }; class base2 { public: base2(){cout<<"class base2"<<endl;} }; class level1:public base2,virtual public base1 { public: level1(){cout<<"class level1"<<endl;} }; class level2:virtual public base1,virtual public base2 { public: level2(){cout<<"class level2"<<endl;} }; class toplevel:public level1,virtual public level2 { public: toplevel(){cout<<"class toplevel"<<endl;} }; void main() { toplevel obj; }
结果
class base1
class base2
class level2
class base2
class level1
class toplevel
标准格式
typedef struct TcpRow { std::string sIp; DWORD dwPort; TcpRow( const std::string &sIp_t = "", const DWORD dwPort_t = 0 ) : sIp(sIp_t), dwPort(dwPort_t) {} }TcpRow; typedef struct TcpRowWithState : virtual public TcpRow { DWORD dwState; TcpRowWithState( const DWORD dwState_t = 0, const std::string &sIp_t = "", const DWORD dwPort_t = 0 ) : TcpRow( sIp_t, dwPort_t ), dwState(dwState_t) {} }TcpRowWithState; typedef struct TcpRowWithProc : virtual public TcpRow { DWORD dwPid; std::string sProcName; TcpRowWithProc( const DWORD dwPid_t = 0, const std::string &sProcName_t = "", const std::string &sIp_t = "", const DWORD dwPort_t = 0 ) : TcpRow( sIp_t, dwPort_t ), dwPid(dwPid_t), sProcName(sProcName_t) {} }TcpRowWithProc; typedef struct TcpRowWithAll : public TcpRowWithState, public TcpRowWithProc { TcpRowWithAll( const DWORD dwState_t = 0, const DWORD dwPid_t = 0, const std::string &sProcName_t = "", const std::string &sIp_t = "", const DWORD dwPort_t = 0 ) : TcpRow( sIp_t, dwPort_t ), TcpRowWithState(dwState), TcpRowWithProc( dwPid_t, sProcName_t ) {} }TcpRowWithAll;