C++的虛函數 Virtual Function

virtual關鍵字有兩個用處。一個是最常見的: virtual關鍵字修飾類的成員函數。另一個是虛基類機制,用於多重繼承。在定義派生類時,要在基類描述前加關鍵字virtual。避免內存中父虛基類成員的重複放置。

虛函數:
定義:虛函數必須是類的 非靜態成員函數 且 非構造函數,其訪問權限是public(可以定義爲private or proteceted,但是對於多態來說,沒有意義)

作用:是實現動態聯編(多態),也就是在程序的運行階段動態的選擇合適的成員函數。通過父類的指針調用實際子類的成員函數。

用法:只需要在聲明函數的類體中使用“virtual”將函數聲明爲虛函數,而定義函數時不需要使用關鍵字“virtual”。

實現原理:
通過一張虛函數表(Virtual Table)來實現的。每一個類有一個虛表(virtual table),內含該class之中有作用的虛函數的地址,然後每個對象有一個vptr,指向虛表(virtual table)的所在。
C++的編譯器應該是保證虛函數表的指針存在於對象實例中最前面的位置(這是爲了保證取到虛函數表的有最高的性能——如果有多層繼承或是多重繼承的情況下)。
1)虛函數按照其聲明順序放於表中。
2)父類的虛函數在子類的虛函數前面。
1)覆蓋的f()函數被放到了虛表中原來父類虛函數的位置。
2)沒有被覆蓋的函數依舊。

虛函數的缺省參數(默認參數):
雖然虛函數是運行期動態綁定的,但是其缺省參數仍然是編譯期靜態綁定的。所以子類指針在調用父類的函數的時候,缺省參數還是用自己的!

name hiding
派生類中對基類虛函數重寫必須把所有重載都重寫一遍比如基類中一個虛函數名稱是重載的2個函數。那麼派生類中若要重寫,必須也重寫2個。

看看virtual函數和構造函數、靜態函數、析構函數、非成員函數、內聯函數、友元函數的關係吧!

爲什麼virtual函數不能是static函數?
static函數都是靜態決議的(編譯的時候就綁定了),virtual函數是動態決議的(運行時候才綁定)。兩個概念是矛盾的。

爲什麼virtual函數不能是構造函數?
虛函數對應一個vtable,vtable是存儲在對象的內存空間的。如果構造函數是virtual的,就需要通過vtable來調用,可是對象還沒有實例化,也就是內存空間還沒有。這就矛盾了。不過編譯是可以通過的。

爲什麼推薦virtual函數做基類的析構函數?
將基類的析構函數定義爲虛函數後,當利用delete釋放一個指向派生類(子類)對象的基類(父類)指針時,系統會調用相應的派生類(子類)的析構函數。而如果不將析構函數定義爲虛函數時,因爲是基類指針,就只調用基類的析構函數。

爲什麼C++不支持普通函數爲虛函數?
      普通函數(非成員函數)只能被overload,不能被override,聲明爲虛函數也沒有什麼意思,因此編譯器會在編譯時邦定函數。

爲什麼C++不支持內聯成員函數爲虛函數?
      其實很簡單,那內聯函數就是爲了在代碼中直接展開,減少函數調用花費的中斷代價,虛函數是爲了在繼承後對象能夠準確的執行自己的動作,這是不可能統一的。(inline函數在編譯時被展開,虛函數在運行時才能動態的邦定函數)。但是,虛函數其實是可以聲明爲inline的,畢竟C++規定在類內部定義的函數都是inline的。但是,你記住,編譯器擁有絕地權利來決定是否展開一個inline函數,雖然這麼定義了,但是編譯器還是去展開它的。

爲什麼C++不支持友元函數爲虛函數?
      因爲C++不支持友元函數的繼承,對於沒有繼承特性的函數沒有虛函數的說法。

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