C 的繼承與多型

2021-09-28 11:27:53 字數 3063 閱讀 3284

◆ 概念介紹

繼承:為了**的重用,保留基類的原本結構,並新增派生類的部分,同時可能覆蓋(overide)基類的某些成員。

多型:一種將不同的特殊行為和單個泛化記號相關聯的能力,分為靜態多型和動態多型。

◆ 繼承:

乙個派生類可以通過繼承獲得基類的所有成員,而無需再次定義它們。分為public、protected和private三種繼承方式,前兩種方式保持基類的所有成員的屬性不變,且派生類可以訪問基類的public和protected成員,但仍然不能訪問基類的private成員;private繼承將使得基類的所有成員在派生類中表現為private屬性。

宣告乙個派生類物件,即在構造派生類物件時,遵循基類的介面,構造基類子物件,構造派生類增加的部分。其中的組成由下圖所示:

當出現菱形繼承時,例如下圖所示:

要構造乙個sleepsofa物件,就要構造乙個sofa和乙個bed子物件,這其中又同時構造了兩次furniture物件,這是不合理的。因此bed和sofa類要對furniture類進行虛繼承(virtual public furniture)來避免這種狀況。

◆ 多型:

靜態多型:在編譯時期就已經確定了的行為,例如帶變數的巨集,模板,函式過載,運算子過載,拷貝構造等。

動態多型:在執行時期才能確定呼叫的行為。例如虛函式呼叫機制。本部分主要討論的是動態多型。虛函式是實現動態多型的機制,其核心理念就是通過基類指標來訪問派生類定義的成員。成員函式在基類為虛函式時,在派生類同樣也是虛函式。純虛函式是指不希望基類物件呼叫的成員函式,需要派生類覆蓋實現這樣的純虛函式。(注:如果某個成員函式在基類中沒有用virtual關鍵字修飾,即普通函式,而在派生類中卻又有完全相同的成員函式宣告,兩個函式即使有相同的名字和相同的引數型別與數量,這兩個函式也是完全不同的函式,因為類的作用域不同)

虛函式表(vtable):每個類都擁有乙個虛函式表,虛函式表中羅列了該類中所有虛函式的位址,排列順序按宣告順序排列,例如這樣兩個類

class base

virtual

void

g() {}

//其他成員

//其他成員

虛表指標(vptr):每個類有乙個虛表指標,當利用乙個基類的指標繫結基類或者派生類物件時,程式執行時呼叫某個虛函式成員,會根據物件的型別去初始化虛指標,從而虛表指標會從正確的虛函式表中尋找對應的函式進行動態繫結,因此可以達到從基類指標呼叫派生類成員的效果。

那麼為什麼需要虛指標和虛函式表來實現動態多型呢?因為無論是什麼函式,包括類內的虛函式和非虛函式,都會儲存在記憶體中的**段。但是當編譯器在編譯時,就可以確定普通函式和非虛函式的入口位址,以及其呼叫的資訊,所以這指的是常量指標。當遇到動態多型時,虛函式真正的入口位址的指標要在執行時根據物件的型別才能確定,所以要通過虛指標從虛函式表中找虛函式對應的入口位址。

當然,用基類指標繫結的子類物件,只能通過這個基類指標呼叫基類中的成員,因為作用域僅限於基類的子物件,子類新增的部分是看不見的。

總結為下面這個例程:

#include using

std::cout;

using

std::endl;

class

base

virtual

void vfun()

};class derive : public

base

virtual

void vfun()

void dfun()

};int

main()

輸出為:

可以看出,bp繫結乙個基類物件,呼叫自己的成員無異議;dp繫結的是乙個子類物件,因此呼叫fun()時,由於dp是乙個基類指標,作用域在於基類中,所以呼叫的是基類的fun(),而呼叫vfun()是通過動態繫結呼叫虛函式表中被子類覆蓋的derive::vfun(),而如果要呼叫dfun()時則會出現編譯錯誤,因為子類獨有成員基類指標不可見。

注:在解有關動態多型的題時,只要把握住一點:這個指標指向的到底是基類物件還是子類物件,如果是基類物件,則呼叫基類的成員函式,如果是子類物件,則要考慮到這個虛成員函式是否被子類中的成員覆蓋掉,即是否產生了動態繫結。另外還有一點,從子類物件強制型別轉換為基類物件是允許的,而相反地要從基類物件強制轉換成子類物件是錯誤的(編譯不通過)。

base* dp1 = new

derive();

derive* dp2 = (derive*) dp1; //

基類指標指向的是子類物件,可以強制轉化為子類指標

base* bp1 = new

base();

derive* bp2 = (base*) bp1; //

錯誤,[error] invalid conversion from 'base*' to 'derive*' [-fpermissive]

//基類指標指向的是基類物件,不能強制轉化為子類指標

C 的繼承與多型

概念介紹 繼承 為了 的重用,保留基類的原本結構,並新增派生類的部分,同時可能覆蓋 overide 基類的某些成員。多型 一種將不同的特殊行為和單個泛化記號相關聯的能力,分為靜態多型和動態多型。繼承 乙個派生類可以通過繼承獲得基類的所有成員,而無需再次定義它們。分為 public protected...

C 繼承與多型

派生類繼承基類 又稱父類 超類 的屬性和方法,在此基礎上可以進行修改或新增新的屬性和方法。class 派生類名 繼承方式 基類名 為了保護基類的資料封裝性,無論哪種繼承方式,積累的私有成員在派生類中都是不可見的。1.public繼承 基類的訪問許可權在派生類中不變。公有還是公有,保護還是保護,派生類...

C 繼承與多型

1 分析菱形繼承的問題。2 剖析虛繼承是怎麼解決二義性和資料冗餘的 首先,我們先來看乙個菱形繼承的程式。為了解決以上問題,我們引入虛繼承的語法。可以有效的解決菱形繼承的資料冗餘和二義性問題。1.虛繼承解決了菱形繼承體系裡子類物件包含多份父類物件的資料冗餘問題和浪費空間的問題。2.虛繼承看起來複雜,但...