C++多繼承

1.繼承的三種方式:函數

公有繼承(public),私有繼承(private),保護繼承(protected)
三種繼承方式的說明,以下表所示:spa

特徵 公有繼承 保護繼承 私有繼承
公有成員變成 派生類的公有成員 派生類的保護成員 派生類的私有成員
保護成員變成 派生類的保護成員 派生類的保護成員 派生類的私有成員
私有成員變成 只能經過基類接口訪問 只能經過基類接口訪問 只能經過基類接口訪問
可否隱式向上轉換

 

 

 

 

                                                                                                                                                                                                                                                                      

2.什麼是多繼承指針

一個類有多個基類,那麼這種繼承關係就叫作多繼承。
好比有兩個類,服務員類Waiter,歌手類Singer,咱們有一個類既是服務員,又是歌手,
那麼咱們能夠定義類的多繼承關係以下:code

class Waiter
{};
class Singer
{};
class SingerWaiter:public Waiter,public Singer
{};

3.使用多繼承會帶來哪些問題
多繼承比單繼承複雜,也更容易出現問題,所以咱們不建議使用多繼承。
多繼承的兩個主要問題是:
1)從兩個不一樣的基類,繼承同名方法
以下例所示:對象

class Waiter
{
public:
    void work(){std::cout<<"Service"<<std::endl;}
};
class Singer
{
public:
    void work(){std::cout<<"Sing"<<std::endl;}
};
class SingerWaiter:public Waiter,public Singer
{};
int main()
{
    SingerWaiter singerWaiter;
    singerWaiter.work();
    return 0;
}

編譯器不知道該調用哪一個基類的work方法,因此會報singerWaiter.work();不明確錯誤。
2)從多個基類間接繼承同一個類的多個實例
以下例所示:blog

class Worker
{};
class Waiter:public Worker
{};
class Singer:public Worker
{};
class SingerWaiter:public Waiter,public Singer
{};
int main()
{
    SingerWaiter singerWaiter;
    Worker *pw = &singerWaiter;
    return 0;
}

會報錯Worker *pw = &singerWaiter;基類Worker不明確。
這是由於SingerWaiter對象建立時,會分別調用Waiter類和Singer類的構造函數,
Waiter類和Singer類又會分別調用Worker類的構造函數,生成了兩份Worker類的實例,
因此pw指針,不知道該指向哪一份Worker實例。繼承

4.如何解決多繼承帶來的問題
對於問題一,咱們在調用時,須要明確指出具體要調用哪一個類的方法,以下所示:接口

int main()
{
    SingerWaiter singerWaiter;
    singerWaiter.Waiter::work();//調用Waiter類的方法
    singerWaiter.Singer::work();//調用Singer類的方法
    return 0;
}

對於問題二,咱們引入了虛基類的概念。以下所示:get

class Worker
{};
class Waiter:virtual public Worker
{};
class Singer:virtual public Worker
{};
class SingerWaiter:public Waiter,public Singer
{};
int main()
{
    SingerWaiter singerWaiter;
    Worker *pw = &singerWaiter;
    return 0;
}

咱們在子類繼承時,聲明一個virtual關鍵字,這時,就代表基類Worker是一個虛基類
實例化SingerWaiter時產生的Waiter對象和Singer對象,共享一個基類Worker對象。編譯器

5.使用虛基類須要注意的問題
咱們知道繼承關係中,類的構造函數具備傳遞性,如如下代碼所示:

class A
{
private:
    int a;
public:
    A(int n=0):a(n){}
    int get(){return a;}
};
class B:public A
{
private:
    int b;
public:
    B(int m=0,int n=0):A(n),b(m){}
    int get(){return b;}
};
class C:public B
{
private:
    int c;
public:
    C(int q=0,int m=0,int n=0):B(m,n),c(q){}
    int get(){return c;}
    void Show()
    {
        std::cout<<A::get()<<" "<<B::get()<<" "<<C::get()<<std::endl;
    }
};
int main()
{
    C c(1,2,3);
    c.Show();
    return 0;
}

輸出結果爲: 3 2 1
調用C的構造函數,則B,A構造函數都將使用傳入的參數進行初始化。

 

咱們再看使用虛基類的狀況:

class A
{
private:
    int a;
public:
    A(int n=0):a(n){}
    int get(){return a;}
};
class B:virtual public A
{
private:
    int b;
public:
    B(int m=0,int n=0):A(n),b(m){}
    int get(){return b;}
};
class C:public B
{
private:
    int c;
public:
    C(int q=0,int m=0,int n=0):B(m,n),c(q){}
    int get(){return c;}
    void Show()
    {
        std::cout<<A::get()<<" "<<B::get()<<" "<<C::get()<<std::endl;
    }
};
int main()
{
    C c(1,2,3);
    c.Show();
    return 0;
}

輸出結果爲: 0 2 1
說明A並無使用傳入的參數進行初始化,
這是由於在虛基類中,咱們假想會有多個子類向虛基類傳遞參數,爲了不這種狀況,
在虛基類的狀況下,禁止了子類C使用中間類B向虛基類A傳遞參數,此時調用的是A的默認構造函數。
那麼咱們該如何使用參數,初始化虛基類呢?
答案是咱們能夠在子類C中,直接調用虛基類A的構造函數進行初始化,如如下代碼所示:

class A
{
private:
    int a;
public:
    A(int n=0):a(n){}
    int get(){return a;}
};
class B:virtual public A
{
private:
    int b;
public:
    B(int m=0,int n=0):A(n),b(m){}
    int get(){return b;}
};
class C:public B
{
private:
    int c;
public:
    C(int q=0,int m=0,int n=0):A(m),B(m,n),c(q){}
    int get(){return c;}
    void Show()
    {
        std::cout<<A::get()<<" "<<B::get()<<" "<<C::get()<<std::endl;
    }
};
int main()
{
    C c(1,2,3);
    c.Show();
    return 0;
}

輸出結果爲: 3 2 1

跳過中間類,直接調用基類的構造函數,這種方式只適合虛基類。
在非虛基類中會報錯「不容許使用間接非虛擬基類」。

 

參考資料:《C++ Primer.Plus》 pp.551-567

相關文章
相關標籤/搜索