C++ 筆記 | 第5課 類的繼承與派生
文章目錄
繼承和派生
繼承與派生 (Derivation and Inheritance) 是 C++ 的重要機制,是面向對象程序設計的重要特徵。該機制自動爲一個類提供來自另一個類的數據結構和操作。這樣可以利用已構造好的類生成新類,充分利用已有資源,進行代碼複用(code reused)。在構造新類的過程中保持已有類的特性稱爲繼承。在已有類的基礎上新增自己的特性而構造新類的過程稱爲派生
// 如
class Rectangle: public Shape {...};
訪問控制
繼承性質 | 基類中成員 (函數) 的訪問權限 | 派生類中成員 (函數) 的訪問權限 |
---|---|---|
public | public protected private |
public protected private |
protected | public protected private |
protected protected private |
private | public protected private |
private private private |
保護成員
對於建立它的類,它與 private
成員的性質相似; 對於繼承此類建立的類,它與 public
成員性質相似
友元與繼承
由於派生類不能直接訪問基類中的私有成員,所以若一個派生類要直接訪問基類中的私有成員,可以將這個派生類聲明爲基類的友元
訪問權限調整
只能恢復原狀, 不能調整訪問權限
public:
Myclass::f; // 恢復 f 的公有屬性
Myclass::g; // 恢復 g 的公有屬性
成員名限定
在派生類和基類中可以聲明同名的成員(函數),不特別說明,在派生類中訪問時,編譯器會默認爲是派生類中的(後出現的),除非用 基類名:: 基類成員名
進行成員名限定,才能調用同名的基類成員(函數)
派生類構造函數與複製構造函數
// 若 B 是基類,A 是派生類,則派生類 A 的構造函數的書寫格式爲:
A::A(參數表): B(參數){...} // 先構造基類無名對象 B
// 派生類 A 的複製構造函數的書寫格式爲:
A::A(const A &p): B(p){...} // 這裏 B 的參數爲 A 類對象 p,
// 用對象 p 中基類部分去複製構造無名基類對象 B
帶基類內嵌對象的派生類
若有類 Point
,私有成員爲 x_
, y_
若要以 Point
作爲基類,生成派生類 Line
,Line
中希望有兩個 Point
對象 (用兩個點來表示平面座標上的一條直線),若想派生兩次 Point
是不允許的. 在 Line
類中加一個 private
成員 Point p;
就行了(另法:Line
類不繼承 Point
類, 私有成員爲兩個 Point
, 函數重新寫(因爲沒有繼承))
動態束定與虛函數
virtual
所修飾的成員函數,被稱爲虛函數。
延遲到程序運行時進行的束定稱爲動態束定 (動態綁定)。
當一個類是從一個或多個類派生出來時,往往會出現多個函數名相同且參數個數和參數類型也完全相同的成員函數,分別實現不同的功能。但這些函數不是重載函數(重載函數必須參數個數或參數類型不同,可以靜態束定),編譯器無法在編譯時靜態束定到底是要調用哪一個成員函數,即使可以靜態束定,往往也不一定符合編程者的真正意願而導致結果錯誤
虛函數延遲到程序運行時再進行動態束定,由 this
指針決定調用 (通過虛函數表(vtable)) 哪一個同名的成員函數
需要在重名的成員函數前加上 virtual
聲明
包含虛函數的類被稱爲多態類
多態性 (polymorphism) 是面向對象的重要特徵之一
類型兼容性規則: 基類指針可以指向它的派生類對象, 反之錯誤。
在派生類中重定義虛函數時,必須滿足:
1 與基類的虛函數參數個數相同;
2 與基類的虛函數參數一一對應;
3 函數返回類型或修飾詞 (如 const
) 與基類的虛函數相同,或都返回指針或引用(可以不同類型)。
若滿足上述三條件,有時可以省略 virtual
關鍵字, 編譯器會自動認爲是同族虛函數。
但從程序可讀性角度考慮,建議保留 virtual
關鍵字
純虛函數
純虛函數是抽象類中的虛函數,在函數名後直接賦值爲 0
, 無函數體。
純虛函數不同於空函數,是抽象的。
純虛函數爲抽象類中的成員函數。
包含純虛函數的類被稱爲抽象類。
抽象類 (無論類中是否有數據成員) 無法實例化,因此不能用抽象類定義對象。
純虛函數聲明格式:
virtual 函數類型 函數名(參數表)=0;
class Figure{ // Figure 是一個抽象類
public:
virtual double getArea()=0;// 純虛函數,爲整個類族提供了通用的外部接口語義};
虛析構函數
在 Figure
類中加入虛析構函數 virtual ~Figure(){}
將 ~Figure()
~Rectangle()
~Triangle()
變爲同族的虛析構函數。
虛析構函數是 C++ 中唯一的函數名不同的虛函數族。
注意: 有虛析構函數,但沒有對應的虛構造函數。
虛析構函數使系統在執行過程中進行析構時,根據 this
指針所指的當前對象,動態決定使用這個對象的析構函數進行正確析構。(否則都掉用基類的析構函數無法執行派生類自己的析構函數導致析構不徹底)
多繼承
class D : public A, protected B, private C
{...}
多繼承的二義性
若出現二義性 (ambiguous) 則需要在相應成員或成員函數前加基類名限定(如 A::
)
虛基類
D 繼承 B 和 C, B 和 C 同時繼承 A, 那麼 D 中會有兩個 A 基類的副本(大多情況下不希望這樣)
C++ 引入了虛基類(virtual base class), 虛基類將使得派生類 D 在繼承間接共同基類 A 時只保留一個 A 的副本。
class A
{...};
class B: virtual public A
{...};
class C: virtual public A
{...};
class D: public B, public C
{...};