在之前的文章中我们讨论了多重继承,留下了一个钻石问题,本文我们将继续讨论这个问题,给出解决问题的方法。
虚基类
通过下面的例子我们来分析钻石问题
class PoweredDevice
{
public:
PoweredDevice(int nPower)
{
cout << "PoweredDevice: " << nPower << endl;
}
};
class Scanner: public PoweredDevice
{
public:
Scanner(int nScanner, int nPower)
: PoweredDevice(nPower)
{
cout << "Scanner: " << nScanner << endl;
}
};
class Printer: public PoweredDevice
{
public:
Printer(int nPrinter, int nPower)
: PoweredDevice(nPower)
{
cout << "Printer: " << nPrinter << endl;
}
};
class Copier: public Scanner, public Printer
{
public:
Copier(int nScanner, int nPrinter, int nPower)
: Scanner(nScanner, nPower), Printer(nPrinter, nPower)
{
}
};
如果创建一个Copier对象,默认情况下会产生2份PoweredDevice的拷贝,一个来时Printer,另外一个来自Scanner。结构如下:

int main()
{
Copier cCopier(1, 2, 3);
}
上面的测试结果:
PoweredDevice: 3
Scanner: 1
PoweredDevice: 3
Printer: 2
如你所见PoweredDeivce被构造了2次。也许有时候你需要这样,但是有时候你只需要一份拷贝。为了共享一个基类,简单的方法就是派生类的继承方式上加上 virtual,这就是所谓的 虚基类。下面的例子会教你如何使用:
class PoweredDevice
{
};
class Scanner: virtual public PoweredDevice
{
};
class Printer: virtual public PoweredDevice
{
};
class Copier: public Scanner, public Printer
{
};
虽然只会构造一个PoweredDevice对象,但是有个疑问就是谁来负责构造它呢?答案是Copier,Copier构造函数有责任来创建PoweredDevice。因此这是Copier直接调用非相邻父类构造函数。
class PoweredDevice
{
public:
PoweredDevice(int nPower)
{
cout << "PoweredDevice: " << nPower << endl;
}
};
class Scanner: virtual public PoweredDevice
{
public:
Scanner(int nScanner, int nPower)
: PoweredDevice(nPower)
{
cout << "Scanner: " << nScanner << endl;
}
};
class Printer: virtual public PoweredDevice
{
public:
Printer(int nPrinter, int nPower)
: PoweredDevice(nPower)
{
cout << "Printer: " << nPrinter << endl;
}
};
class Copier: public Scanner, public Printer
{
public:
Copier(int nScanner, int nPrinter, int nPower)
: Scanner(nScanner, nPower), Printer(nPrinter, nPower), PoweredDevice(nPower)
{
}
};
结果:
PoweredDevice: 3
Scanner: 1
Printer: 2
PoweredDevice仅仅构造了一次,我们还需要注意的一些细节问题:
首先,虚基类在非虚基类之前创建,这样确保了所有基类在他们的派生类之前被创建。
其次,Scanner和Printer的构造函数依然会调用PoweredDevice的构造函数,如果我们创建的是Copier实例,这些调用将会被忽略,因为Copier是创建PoweredDevice的责任人,而不是Scanner和Printer,但是如果我们创建Scanner或者Printer实例,virtual关键字将会被忽略,正常的继承规则将适用,对PoweredDevice的构造函数调用将起作用。
最后,如果一个类继承自一个或者多个类(这些类有虚父类),那么最底层派生类负责构造虚基类。另外,这也适用于单一继承情况:如果Copier单一继承自Printer,Printer虚拟继承自PoweredDevice,Copier依然负责构造PoweredDevice。