在之前的文章中我們讨論了多重繼承,留下了一個鑽石問題,本文我們将繼續讨論這個問題,給出解決問題的方法。
虛基類
通過下面的例子我們來分析鑽石問題
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。