C 程式設計 虛函式與多型

2021-07-25 15:44:27 字數 2359 閱讀 8215

在《 c++ 程式設計思想》一書中對虛函式的實現機制有詳細的描述,一般的編譯器通過虛函式表,在編譯時插入一段隱藏的**,儲存型別資訊和虛函式位址,而在呼叫時,這段隱藏的**可以找到和實際物件一致的虛函式實現。

我們在這裡提供乙個 c 中的實現,模仿 vtable 這種機制,但一切都需要我們自己在**中裝配。

之前在網上看到一篇描述 c 語言實現虛函式和多型的文章,談到在基類中儲存派生類的指標、在派生類中儲存基類的指標來實現相互呼叫,保障基類、派生類在使用虛函式時的行為和 c++ 類似。我覺得這種方法有很大的侷限性,不說繼承層次的問題,單單是在基類中儲存派生類指標這一做法,就已經違反了虛函式和多型的本意——多型就是要通過基類介面來使用派生類,如果基類還需要知道派生類的資訊……。

我的基本思路是:

好了,先來看一點**:

[cpp]view plain

copy

struct

base ;  

struct

derived ;  

struct

derived_2;  

上面的**是我們接下來要討論的,先說一點,在 c 中,用結構體內的函式指標和 c++ 的成員函式對應, c 的這種方式,所有函式都天生是虛函式(指標可以隨時修改哦)。

注意,derived 和 derived_2 並沒有定義 func_1 和 func_2 。在 c 的虛函式實現中,如果派生類要重寫虛函式,不需要在派生類中顯式宣告。要做的是,在實現檔案中實現你要重寫的函式,在建構函式中把重寫的函式填入虛函式表。

我們面臨乙個問題,派生類不知道基類的函式實現在什麼地方(從高內聚、低耦合的原則來看),在構造派生類例項時,如何初始化虛函式表?在 c++ 中編譯器會自動呼叫繼承層次上所有父(祖先)類的建構函式,也可以顯式在派生類的建構函式的初始化列表中呼叫基類的建構函式。怎麼辦?

我們提供乙個不那麼優雅的解決辦法:

每個類在實現時,都提供兩個函式,乙個建構函式,乙個初始化函式,前者使用者生成乙個類,後者用於繼承層次緊接自己的類來呼叫以便正確初始化虛函式表。依據這樣的原則,乙個派生類,只需要呼叫直接基類的初始化函式即可,每個派生類都保證這一點,一切都可以進行下去。

下面是要實現的兩個函式:

[cpp]view plain

copy

struct

derived *new_derived();  

void

initialize_derived(

struct

derived *d);  

new 開頭的函式作為建構函式, initialize 開頭的函式作為 初始化函式。我們看一下 new_derived 這個建構函式的實現框架:

[cpp]view plain

copy

struct

derived *new_derived()    

如果是 derived_2 的建構函式 new_derived_2,那麼只需要呼叫 initialize_derived 即可。

說完了建構函式,對應的要說析構函式,而且析構函式要是虛函式。在刪除乙個物件時,需要從派生類的析構函式依次呼叫到繼承層次最頂層的基類的析構函式。這點在 c 中也是可以保障的。做法是:給基類顯式宣告乙個析構函式,基類的實現中查詢虛函式表,從後往前呼叫即可。函式宣告如下:

[cpp]view plain

copy

struct

base ;  

說完構造、析構,該說這裡的虛函式表到底是怎麼回事了。我們先畫個圖,還是以剛才的 base 、 derived 、derived_2 為例來說明,一看圖就明白了:

我們假定 derived 類實現了三個虛函式, derived_2 類實現了兩個,func_2 沒有實現,上圖就是 derived_2 的例項所擁有的最終的虛函式表,表的長度( vt_size )是 9 。如果是 derived 的例項,就沒有表中的最後三項,表的長度( vt_size )是 6 。

必須限制的是:基類必須實現所有的虛函式,只有這樣,這套實現機制才可以運轉下去。因為一切的發生是從基類的實現函式進入,通過遍歷虛函式表來找到派生類的實現函式的。

當我們通過 base 型別的指標(實際指向 derived_2 的例項)來訪問 func_1 時,基類實現的 func_1 會找到 vtable 中的 derived_2_func_1 進行呼叫。

好啦,到現在為止,基本說明白了實現原理,至於 初始化函式如何裝配虛函式表、基類的虛函式實現,可以根據上面的思路寫出**來。按照我的這種方法實現的虛函式,通過基類指標訪問,行為基本和 c++ 一致。

C 程式設計之虛函式與多型

多型性是指乙個名字,多種語義 或介面相同,多種實現。過載函式是多型性的一種簡單形式。虛函式允許函式呼叫與函式體的聯絡在執行時才進行,稱為動態聯編。基類指標雖然獲取派生類物件位址,卻只能訪問派生類從基類繼承的成員。示例如下 includeusing namespace std class base v...

C 虛函式與多型

1.1 虛函式概念 1.定義 在乙個類的成員函式前面加上virtual關鍵字,則該函式就稱為虛函式。2.如果乙個函式不是類的成員函式,則該函式不能定義為虛函式。即就是類外面不能使用virtual關鍵字 1.2 純虛函式與抽象類 1.純虛函式 在虛函式的後面加上 0 virtual void disp...

c 多型與虛函式

多型按字面的意思就是多種形態。當類之間存在層次結構,並且類之間是通過繼承關聯時,就會用到多型。c 多型意味著呼叫成員函式時,會根據呼叫函式的物件的型別來執行不同的函式。下面的例項中,基類 shape 被派生為兩個類,如下所示 include using namespace std class sha...