繼承

1.什麼是繼承
繼承就是在原有類的基礎上進行拓展,增加功能,實現了代碼複用,體現了面嚮對象語言的層次結構。
2.繼承的定義
繼承即通過派生權限(方式)由基類派生一個類,子類中豐富了基類中的功能
繼承
-------------------------如這張圖所示,這就是定義一個派生類的方式。訪問權限有三種,分別是
public(類內類外都可以訪問)、
protect(類內可以訪問、子類中可以訪問、類外不能訪問)、
private(只有類內可以訪問、子類中也不能訪問、類外更不能訪問)
-------------------------同樣的,繼承方式也有同上的三種
下面的成員變量指的是包括成員函數和成員變量在內的所有成員
我們來討論一下這三種繼承方式有何區別---------->
public:
①:基類中的public和protect修飾的成員變量通過public繼承到子類後依然是其在基類中的訪問權限,即基類中的protect成員變量通過繼承到子類後在子類中依然是protect權限。
②:基類中的private修飾的成員變量在子類中不可見。
protect:
①:基類中public修飾的成員變量繼承到子類中降級爲protect,protect繼承到子類中依然爲protect。
②:基類中的private修飾的成員變量在子類中不可見。
private:
①:基類中public修飾的成員變量繼承到子類中降級爲private,protect繼承到子類降級爲private。
②:基類中的private修飾的成員變量在子類中不可見。
注意:
1.基類中的private成員變量在繼承到子類時都是不可見的,但這不代表沒有繼承,繼承是將基類中的所有成員都繼承到子類,可以通過sizeof的方式進行驗證,這裏的不可見指的是子類中存在這個成員變量但是基類不允許子類去訪問和修改它,即對於子類來說是不可見的。
2.class默認的繼承方式是private,struct默認是public。


看到這裏那麼基類和子類的區別就出來了即子類實際是對基類完成了一份拷貝後再豐富自己的內容。
那麼我們就可以得到下面的推論,派生類對象 可以賦值給基類的對象 / 基類的指針/ 基類的引用。而反過來則不行,道理很簡單,說白了派生類在內存中的大小肯定大於或等於基類(一般是大於,如果等於說明派生類和基類一樣,那麼創建這個派生類就沒有什麼意義了),所以大給小賦值沒有問題,反過來如果是小給大賦值那麼一定不會都賦值進去,那麼剩餘的部分就沒辦法了,但可以強制類型轉換進行賦值(但強烈不推薦!)。


基類和子類分別代表着不同的作用域:
如果我們在基類和子類中創建相同名字的成員變量,在創建子類對象後訪問到的是基類中的成員變量還是子類中的成員變量呢?這種現象叫做隱藏,通過測試會發現由子類創建的對象優先訪問的是子類中的同名成員變量,如果我們向訪問基類中的同名成員變量需要加訪問限定符,即

S.Person::id = 10;

這樣就把基類中的id修改爲10了。
注意:這裏隱藏的定義是同名即構成同名隱藏,與變量的類型、成員函數的參數列表和返回值都沒有關係。


那麼派生類在定義完成時其內也有六個默認函數,同普通類中的六個默認函數相同-->
派生類對象在使用這些函數時都要先調用基類中的對用函數完成對繼承下來的基類部分的初始化。
注意:
1、派生類中會生成無參的默認構造函數,若基類中自定義了非默認構造函數(無參、全缺省),在派生類中也必須自定義構造函數並在初始化列表對其初始化
2.拷貝構造函數與構造函數類似
3.賦值運算符
4.析構
5.取地址
6.const取地址
說明:在創建一個派生類對象時,首先調用派生類的構造函數,並在初始化列表完成對繼承下來的基類部分內容的初始化,在初始化派生類以及的內容,在析構時遵循後創建的先析構,所以先調用派生類對象的析構函數,後再對基類對象調用析構函數。這是創建派生類對象的過程!仔細理解!!!


在繼承的時候基類的友元函數是不會發生繼承的。因爲友元函數不屬於類啊!
而static修飾的基類成員變量會繼承到其派生類中而且依然共用一個地址,所有的子類中都擁有這個靜態變量,和static的定義沒有出入,則整個繼承體系裏面只有一個這樣的成員。


菱形繼承和菱形虛擬繼承

單繼承和多繼承的區別:多繼承即一個子類繼承於兩個基類擁有這兩個基類中的屬性,特殊的一種繼承關係是菱形繼承,首先畫一張圖來看單繼承的對象模型:
繼承
即派生類中從父類繼承下來的部分在上,獨有的部分在下。
這個不難理解,我們再來看看菱形繼承的對象模型:
繼承
C1和C2分別繼承自基類B,D同時繼承C1和C2,這樣構成了菱形繼承,但是這種情況很特殊,即D類中有兩個_b對象,_這樣會造成數據冗餘,也是造成二義性的原因,我們可以通過訪問限定符來指定訪問_b,_但並沒有根本性的解決這個問題,所以有了虛擬繼承的概念,即用virtual關鍵字修飾C1、C2函數,使他們只繼承一次B中的_b_成員,我們再來通過觀察內存中的情況看看菱形虛擬繼承的對象模型:
繼承
通過這張圖我們可以看出b在d對象中只保存了一份,而在C1和C2中分別增加了兩個虛基表指針通過訪問虛基表來找到b,虛基表中存放的就是自身的偏移量和相對基類偏移量,這兩個虛基表指針都可以訪問到b,一般情況下是通過C1的虛基表指針找到b。這樣就解決了菱形繼承中存在的缺陷。
看到這就體現出了c++語法的複雜性,所以我們一般不建議設計出多繼承,更不建議設計菱形繼承。

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