多重繼承
前面我們介紹的派生類只有一個基類,稱為單基派生或單一繼承。在實際運用中,我們經常需要派生類同時具有多個基類,這種方法稱為多基派生或多重繼承。
2.1 多重繼承的聲明:
在 C++ 中,聲明具有兩個以上基類的派生類與聲明單基派生類的形式類似,只需將要繼承的多個基類用逗號分開即可。
在多重繼承中,公有派生和私有派生對于基類成員在派生類的可訪問性與單繼承的規則相同。
另外,對基類成員的訪問必須是無二義的,若兩個基類中具有同名的數據成員或成員函數,使用成員名限定來消除二義性,若派生類中新增成員或成員函數與基類成員或成員函數同名,則派生類會覆蓋外層同名成員,也須使用作用域分辨符。
2.2 多重繼承的構造函數和析構函數:
多重繼承的構造函數的定義形式與單繼承構造函數的定義形式類似,只有 n 個基類的構造函數之間用“,”分隔。
多重繼承的構造函數的執行順序與單繼承構造函數的執行順序相同,也是遵循先執行基類的構造函數,再執行對象成員的構造函數,最后執行派生類構造函數的原則。在多個基類之間,則嚴格按照派生類聲明是從左到右的順序來排列先后。而析構函數的執行順序與構造函數的執行順序相反。
2.3 虛基類 :
如果某個派生類的部分或全部直接基類是從另一個共同的基類派生而來,在這些基類中,從上一級基類繼承來的成員就有相同的名稱,則在這個派生類中訪問這個共同的基類中的成員時,可能會產生二義性,此時,可定義虛基類。這就要求在其直接基類的定義中,使用關鍵字 virtual 將那個共同的基類定義為虛基類,其語法形式如下:
class 派生類名: virtual 派生方式 基類
虛基類的初始化與一般的多重繼承的初始化在語法上是一樣的 ,但構造函數的調用順序不同,虛基類構造函數的調用順序是這樣規定的:
1) 在同一層次中,先調用虛基類的構造函數,接下來依次是非虛基類的構造函數,對象成員的構造函數,派生類的構造函數。
2) 若同一層次中包含多個虛基類,這些虛基類的構造函數按對他們說明的先后次序調用
3) 若虛基類由非虛基類派生而來,則仍然先調用基類構造函數,再調用派生類構造函數。
?
上面是理論,有點兒晦澀,下面舉一個例子.以下圖為例:
接口Common是所有類的公共接口,Server接口繼承Common接口,Client接口繼承Common接口,CommonImpl是Common接口的一個公共實現。ServerImpl是Server接口的實現,它的Common接口的實現直接使用CommonImpl類的實現。
如果在C#中,這個問題會自然而優雅的解決,即編譯器會知道,如果1個類繼承了Server接口,又繼承了CommonImpl實現,那么ServerImpl只需要實現Server接口中特定的方法即可。唯一的限制是CommonImpl必須放在Server接口的前面。看下面的演示代碼:
using System;
using System.Collections.Generic;
using System.Text;namespace Demo
{interface IA{string funcInA();}interface IB : IA{string funcInB();}public class CA : IA{#region IA 成員public string funcInA(){return "CA.funcInA";}#endregion}public class CB : CA, IB{#region IB 成員public string funcInB(){return "CB.funcInB";}#endregion}}
類CB只需要實現接口IB中的方法即可。CA,IB必須以這個順序出現;
?
對于C++而言,編譯器是不知道的,如果你希望所有子類共享一個基類,或者子類都共用一個通用實現的基類,那么你需要把共享的那部分,聲明為virutal基類,上圖中的例子,就必須把Common的子類繼承,聲明為虛基類繼承。
#pragma once
#include <iostream>
using namespace std;class IA
{
public:virtual int FuncInA() =0;
};class IB:virtual public IA
{
public:virtual int FuncInB()=0;
};class CA:virtual public IA
{
public:virtual int FuncInA(){cout<<" CA::FuncInA "<<endl;return 1;};
};class CB:public CA,public IB
{
public:int FuncInB(){cout<<"CB::FuncInB"<<endl;return 2;};/*virtual int FuncInA()
{
cout<<"CB::FuncInA"<<" Call CA::FuncInA() ==>"<<CA::FuncInA()<<endl;
return 3;
};*/};
上面的代碼編譯可以通過,且結果如期望。如果去掉virtual繼承修飾符,就會報錯。