c++之各種繼承(單繼承,多繼承,菱形繼承)

基本簡介

在c++語言中。,一個派生類可以從一個基類派生,也可以從多個基類派生。從一個基類派生的繼承稱爲單繼承,從多個基類派生的繼承稱爲多繼承。繼承可以使代碼得到複用,子類還可以在父類的基礎上添加功能
c++繼承分爲公有繼承,私有繼承,保護繼承三類
單繼承的定定義格式:
class <派生類名>:<繼承方式><基類名>
{
<派生類新定義成員>
}
多繼承的定定義格式:
class <派生類名>:<繼承方式1><基類名1>,<繼承方式2><基類名2>…
{
<派生類新定義成員>
}
可見,多繼承與單繼承的區別從定義格式上看,主要是多繼承的基類數量上的區別,如果省略繼承方式,對‘class’將採用私有繼承,對‘struct’將採用公有繼承

繼承方式

  1. 公有繼承:
    公有繼承的特點是基類的共有成員和保護成員作爲派生類的成員的時候,他們都保持原有的狀態,而基類的私有成員仍然是私有的,不能被派生類訪問。
  2. 私有繼承
    私有繼承的特點是基類的共有成員和保護成員都作爲派生類的私有成員,並且不能被這個派生類的子類所訪問。
  3. 保護繼承:
    保護繼承的提誒那是接力的所有共有成員和保護成員都稱爲派生類的保護成員,並且只能被它的派生類成員函數或者友元訪問,基類的私有函數仍然是私有的。
    下面列出三種不同的繼承凡是的基類特性和派生類的特性
public protected privated
公有繼承 public protect 不可見
私有繼承 private private 不可見
保護繼承 protected protected 不可見

爲了進一步理解三種不同的繼承方式在其成員的可見性方面的區別,下面從三種不同角度進行討論。

公有方式

  1. 基類成員對其對象的可見性:公有成員可見,其他不可見。保護成員同私有成員。
  2. 基類成員對派生類的可見性:公有成員和保護成員可見,而私有成員不可見,保護成員同私有成員。
  3. 基類成員對派生類對象的可見性:公有成員可見,其他成員不可見。
    所以,在公有繼承時,派生類的對象可以訪問基類中的公有函數;派生類的成員函數可以訪問基類的共有成員和保護成員。這裏,一定套區分清楚派生類的對象和派生類中的成員函數對基類的訪問時不同的。

私有方式

  1. 基類成員對其對象的可見性:公有成員可見,其他不可見。
  2. 基類成員對派生類的可見性:公有成員和保護成員可見,而私有成員不可見。
  3. 基類成員對派生類對象的可見性:所有成員都不可見。
    所以,在私有繼承時,基類的成員只能由派生類中的成員訪問,而且不玩再往下繼承

保護方式

這種繼承方式與私有繼承情況相同,區別僅在於對派生類的成員而言,對基類成員有不同的可見性。
下面用一個實例代碼來更加清晰的瞭解基類成員對其對象,對派生類,對派生類對象的可見性

#include<iostream>
using namespace std;
class base
{
    base()
    {
        a=0;
        b=1;
        c=2;
    }
    int a;
protected:
    int b;
private:
    int c;
}
class derived1 : public base//公有繼承
{
    void fun()
    {
    cout<<base::a<<base::b<<base<<endl;//可以訪問公有和保護
    //cout<<base::c; //不能訪問
    }
}
class derived2 : private base//私有繼承
{
    void fun()
    {
    cout<<base::a<<base::b<<base<<endl;//可以訪問公有和保護
    //cout<<base::c; //不能訪問
    }
}
class derived3 : protected base//保護繼承
{
    void fun()
    {
    cout<<base::a<<base::b<<base<<endl;//可以訪問公有和保護
    //cout<<base::c; //不能訪問
    }
    class derived4 : public derived3//不能被private繼承的派生類的子類訪問
{
    void fun()
    {
    //cout<<base::a<<base::b<<base<<endl;
    //cout<<base::c; 
    }
}
class derivated5 : base//默認是private繼承
{
    void fun()
    {
    cout<<base::a<<base::b<<endl;
    //cout<<base::c;
    }
}
int main()
{
    base b1;
    derived1 d1;
    derived2 d2;
    derived3 d3;
    d1.fun();
    d2.fun();
    d3.fun();
    cout<<b1.a//base class object 只能訪問public member
    cout<<d1.a//public 繼承是derived class object 只能訪問base class的public member
    //cout<<d1.b<<d1.c;//不能訪問
    //cout<<d2.a<<d2.b//protected 繼承,derived class object 不能訪問base class member
    //cout<<d3.a;//private繼承時 derived class object 不能訪問 base class的member
    return 0;
}
}

關於菱形繼承(虛繼承)

虛繼承是一個基類(base)派生出兩個類(derived1,derived2)之後有一個類(dderived)繼承derived1和derived2才使用的,如果按照原來的繼承進行,當需要調用到base類的成員的時候,編譯器會報錯,因爲編譯器不知道是通過derived1去調用base還是通過derived2去調用
面對上面的問題,我們的解決辦法是
直接在dderived中需要base時,用一個對象去指定是通過derived1還是derived2去調用base
但是這種方法存在缺陷,浪費了一個derived的空間不說(冗餘),dderived存在兩個base成員(存在二義性問題),和需求不同,這個問題就比較嚴重了。
之後就引入了虛函數的概念
只需要在兩個derived1和derived2 定義時前面加上關鍵字virtual就變成了虛擬繼承
內存分佈就變成:
derived1 虛表指針
derived1成員
derived2 虛表指針
derived2 成員
dderived 成員
base 成員
虛表指針裏面存放虛函數的地址,如果子類存在將父類的虛函數重寫,將會把這個函虛表可以繼承,如果子類沒有重寫虛函數,那麼子類虛表中仍然會有該函數的地址,只不過這個地址指向的是基類的虛函數實現。如果基類有3個虛函數,那麼基類的虛表中就有三項(虛函數地址),派生類也會有虛表,至少有三項,如果重寫了相應的虛函數,那麼虛表中的地址就會改變,指向自身的虛函數實現。如果派生類有自己的虛函數,那麼虛表中就會添加該項。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章