C++學習第13篇-虛擬函數

來源:互聯網
上載者:User

1. 衍生類中基類的指標和引用

上一篇中,已經介紹了類的繼承;這篇中,將介紹繼承中另一個重要和實用的方面-虛擬函數。

在討論虛擬函數之前,我們應明確為什麼需要虛擬函數。之前我們知道,衍生類包含了基類的一部分和本身的一部分。


輸出:


因為一個Derive的對象也是Base類型,如老師是一個人。


輸出:


因為rBase和pBase是Base類型,只能看到Base類的成員或者是Base類繼承其他類的成員,對於Derived類的非繼承成員是不可見的。

另一個例子:



輸出:


2)在基類中使用指標和引用

如果沒有使用指標,對於多個動物的呼叫:


當然也可以這樣寫:


但是,Animal只會執行其對應的呼叫函數。

測試:


而且,當動物種類超出30種時,需要30個數組來儲存不同的動物種類。


這兩種方法都有個明顯的缺點:基類指標指向衍生類,執行的函數是基類的函數,而不是衍生類的重定義函數。

2. 虛擬函數

在第1節中,使用基類指標,可以簡化代碼;但是基類指標只能執行基類的對應函數,而不是衍生類的重定義函數。

例如:


輸出是:rBase is a Base。

這個問題可以通過虛擬函數來解決:

虛擬函數-特殊類型的函數,執行相同簽名函數的最進階衍生版本。

只需將virtual關鍵字放在函式宣告的前面。

修改上面的例子:

輸出是:rBase is a Derived。

因為使用virtual關鍵字,GetName執行力衍生類版本的函數;

複雜一點的例子:

輸出是:rBase is a C。

流程:首先rBase是一個A引用,因為A中的GetName是一個虛擬函數;然後執行遍曆A到C的相同簽名的函數,最後執行最進階衍生的C類,然後執行C的重定義函數。

因為rBase指向的是一個C對象,所以不會遍曆D的函數。

更複雜的例子:

輸出:

再次測試:

輸出:

注意:衍生類函數的簽名必須和基類的虛擬函數的簽名一致,才能達到執行衍生類函數的效果。

2)virtual關鍵字的使用

A)事實上,virtual關鍵字在衍生類中不必要使用的。

B)通常,最原始的基類使用virtual關鍵字,這樣所有繼承的衍生類都可以虛擬執行;

C)通常,推薦在衍生類中也使用virtual關鍵字,儘管文法上不需要;

3)虛擬函數的傳回值

在正規情況下,虛擬函數和衍生類的重定義函數必須有一致的傳回型別;

但是,例外:如果虛擬函數返回的是類的指標或引用,那麼重定義函數可以返回衍生類的指標或引用。

3. 虛擬解構函式、虛擬賦值、重寫虛擬列表

1)虛擬解構函式

C++提供了一個預設的解構函式,有時需自訂解構函式,特別是在動態分配記憶體。

例如:


這時,列印:Calling ~Base()。

如果採用虛擬解構函式:


此時,輸出是:

Calling ~Drived

Calling ~Base()

2)虛擬賦值

將賦值虛擬是可能的。不像虛擬析構,虛擬賦值是麻煩之源。

3)重寫虛擬化

使用全域運算子::來顯示執行基類函數:


這種做法是比較少使用,但只是說明是可能的。

虛擬函數的缺點:低效的,執行一個虛擬函數需要的時間更長;還要另外開闢記憶體空間。

4. 提前綁定和延遲綁定

當CPU執行編譯器時,每行語句被編譯成一行或多行的電腦代碼,每一行指定了唯一的順序地址;函數也一樣,每個函數結尾都有一個唯一的順序地址。

所謂綁定,就是將標誌符轉換為機器語言地址的過程;本章節主要講述函數的綁定。

1)提前綁定

當執行直接調用一個函數時,這個進程成為提前綁定(靜態繫結);記住,每個函數都有唯一機器地址,當編譯器遇到函數調用,編譯器將函數調用轉換為機器語言,CPU跳轉到函數的地址。


因為Add、Subtract和Multiply是直接函數調用,即提前綁定。

2)延遲綁定

在一些程式中,不可能知道那些函數直到運行時才載入,即延遲綁定(動態綁定);

在C++中實現消極式載入主要是採用函數指標;

修改上面的main函數:


因為pFcn知道運行時才確定執行的調用函數,所以稱作延遲綁定。

延遲綁定效率相對低一點,因為要涉及更多的間接調用級數;但延遲更靈活,因為函數調用知道運行時才確定。

5. 虛擬表

在應用虛擬函數,C++使用了延遲綁定的特殊形式-虛擬表。

虛擬表是延遲管理器管理的一張函數調用列表。名字如:“vtable”, “virtual functiontable”, “virtual method table”, or “dispatch table”。

虛擬表是編譯器在編譯時間建立的一個靜態數組;每個函數調用都有一個入口供對象調用。

編譯器為基類添加類一個隱藏的指標: *__vptr;這是個真實的指標,衍生類可以繼承。

例如:


編譯器建立3張虛擬表,記錄每個類的虛擬函數;

每個虛擬表的*_vptr負責管理函數調用的遍曆;

通過虛擬表,編譯器和程式更準確地找到所需的虛擬函數。

6. 純粹虛擬函數、抽象基類和介面類

這是虛擬函數的最後一節,恭喜各位通過了C++語言最難懂的部分了。

1)純粹虛擬(抽象)函數和抽象基類

目前為止,所看到的虛擬函數都有函數體(定義體),C++允許建立一類特殊的虛擬函數-純粹虛擬函數(抽象函數),沒有函數體。


使用一個純粹的虛擬函數有2個主要的結果:

A)任何類使用了一個或多個的純粹虛擬函數,成為抽象基類;抽象類別-即不可以執行個體化;

B)任何繼承於抽象基類的衍生類必須實現該函數;

一個純粹的虛擬函數是非常有用的,我們想將一個函數放到基類,衍生類只需知道其傳回型別即可;這樣就預防了衍生類忘記實現該函數;

2)介面類

一個介面類-不包含任何成員變數,所有函數都是純粹虛擬函數。

介面是非常有用的,當你想定義衍生類必須實現的功能;但功能的實現細節依賴於具體的衍生類。


介面類越來越流行,因為其易用、易擴充和易維護。

【免責特此聲明:
1)本內容可能是來自互連網的,或經過本人整理的,僅僅代表了互連網和個人的意見和看法!
2)本內容僅僅提供參考,任何參考該內容造成任何的後果,均與原創作者和本部落格作者無關!】

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.