【C++】c++中的繼承

1、什麼是繼承?
我們都知道繼承性在客觀世界中是一種常見的現象。在我們瞭解C++的繼承之前,讓我們先介紹一個現實世界中“動物”的例子,它們之間的屬性繼承關係如圖所示:
這裏寫圖片描述
從面向對象的觀點來看,繼承所表達的正是這樣一種類與類之間的關係,這種關係允許既有類在的基礎上創建新類,增加了軟件的重用性,減少了工作量。
繼承方式不同所引起的訪問控制關係變化:
這裏寫圖片描述
2、基類與派生類
C++允許從一個類“派生”其他的類,當然即將派生的新類與既有類之間是存在一定的內在聯繫的。可以按如下描述形象地理解“派生”:父輩類生下了“兒子”類,例如,由於“哺乳動物”類是“有脊椎動物”類的一個特例,所以可以從“有脊椎動物”類派生出“哺乳動物”類。在這個派生過程中,“下一級的類”繼承了“上一級”的屬性。
在繼承關係裏面,在派生類中如果沒有顯示定義這六個成員函數,編譯系統則會默認合成這六個默認成員函數:
這裏寫圖片描述
這裏我們還應注意幾點問題:
(1).public繼承是一個接口繼承,保持is-a原則,每個父類可用的成員對子類也可用,因爲每個子類對象也都是一個父類對象。(注意:可以把基類對象賦值給派生類,但不可以把派生類對象賦值給基類)
(2). protected/private繼承是一個實現繼承,基類的部分成員並非完全成爲子類接口的一部分,是 has-a 的關係原則,所以非特殊情況下不會使用這兩種繼承關係,在絕大多數的場景下使用的都是公有繼承。私有繼承以爲這is-implemented-in-terms-of(是根據……實現的)。通常比組合(composition)更低級,但當一個派生類需要訪問基類保護成員或需要重定義基類的虛函數時它就是合理的。1
(3).不管是哪種繼承方式,在派生類內部都可以訪問基類的公有成員和保護成員,基類的私有成員存在但是在子類中不可見(不能訪問)。
3、單繼承&多繼承&菱形繼承1
繼承的一般形式爲:

class 派生類名:(繼承方式)基類名
{
派生類新增成員;
}

【單繼承】
一個類只有一個直接父類時稱這個繼承關係爲單繼承。

class Person
{
protected:
    string _name;
};
class Student : public Person
{
protected:
     int _stuNum; //學號
};
class Granduate : public Student
{
protected:
string _seminarCourse; //研究科目
}

這裏寫圖片描述
【多繼承】
即一個派生類繼承多個基類。
這裏寫圖片描述
例如下面的例子:類Z就是一個多重繼承的派生類,它有兩個基類,分別是類X和類Y.

class X
{
public:
   X(int n);
   ~X();
protected:
   int _x;
};
class Y
{
public:
   Y(double d);
   ~Y();
protected:
   int _y;
};
class Z : public X,public Y 
{
public:
   Z(int n,double d);
   ~Z();
protected:
   int _z;
};

【菱形繼承】
這裏寫圖片描述

#include<iostream>
using namespace std;
class A
{
public:
int _a;
};
class X : public A
{
public:
    void fun1()
    {}
protected:
int _x;
};
class Y : public A
{
public:
    void fun1()
    {}
protected:
int _y;
};
class Z : public X,public Y
{
protected:
int _z;
};
int main()
{
    Z* obj = new Z();
    obj->fun1();       //錯誤
}

這裏寫圖片描述

由於Z從X和Y繼承,因此當調用函數fun1()時,編譯器就不知道調用類X還是類Y的成員,因此編譯器會輸出如下錯誤:
這裏寫圖片描述
我們可以這樣解決:

void main()
{
    Z* obj = new Z();
    obj->X::fun1();      //明確指定調用X的成員函數
    obj->X::_a();    //顯示指定哪個父類成員
    obj->Y::_a();    
}

這裏寫圖片描述

我們可以通過虛繼承更好的解決這個問題。
虛繼承–解決菱形繼承的二義性和數據冗餘的問題
1. 虛繼承解決了在菱形繼承體系裏面子類對象包含多份父類對象的數據冗餘&浪費空間的問題。
2. 虛繼承體系看起來好複雜,在實際應用我們通常不會定義如此複雜的繼承體系。一般不到萬不得已都不要定義菱形結構的虛繼承體
繫結構,因爲使用虛繼承解決數據冗餘問題也帶來了性能上的損耗。
這裏寫圖片描述
這裏寫圖片描述

4、特別注意:
(1)友元關係不能繼承,也就是說基類友元不能訪問子類私有和保護成員。

class Person
{
friend void Display(Person &p , Student&s);
protected :
string _name ; // 姓名
};
class Student: public Person
{
protected :
int _stuNum ; // 學號
};
void Display(Person &p , Student &s)
{
cout<<p._name<<endl;
cout<<s._name<<endl;
cout<<s._stuNum<<endl;
}
void TestPerson1()
{
Person p;
Student s;
Display (p, s);
}

將會出現如下報錯:
這裏寫圖片描述
(2)基類定義了static成員,則整個繼承體系裏面只有一個這樣的成員。無論派生出多少個子類,都只有一個static成員實例。
剖析虛繼承是怎麼解決二義性和數據冗餘的

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