林洋
MIUI部門
負責recovery模式,ota升級
在一次修改代碼過程中踩的坑,下來研究了一下,發現C++中虛函數重載後會產生很多有意思的情況,在這裡總結了一下。
C++中有重載(overload)和重寫(override)以及重定義這幾個概念。
1 overload:指的是相同作用域中的兩個函數的函數名相同,但參數列表的個數、順序、類型不同。而override指的是子類中重新定義的父類的虛函數;
2 override:overload要求兩個函數的參數列表必須不同,但是不要求這兩個函數必須是虛函數。而override要求必須是虛函數且父類的虛函數必須有virtual關鍵字,函數的參數列表和返回值也必須相同。子類中override的虛函數的訪問修飾符可以不同;
3 重定義也是描述分別位於父類與子類中的同名函數的,但返回值可以不同。如果參數列表不同,這時子類中重定義的函數不論是否有virtual關鍵字,都會隱藏父類的同名函數。
如果參數列表相同,但父類中的同名函數沒有virtual關鍵字修飾,此時父類中的函數仍然被隱藏。
虛函數的典型用法是:
程序的輸出是:
那麼如果虛函數之間又發生了overload,會出現什麼情況?
我們先看最簡單的情況:
編譯上面的代碼,會發生如下錯誤:
這就是因為父類中虛函數的參數列表已經發生變化,這時不論子類中重定義的函數不論是否有virtual關鍵字,都會隱藏父類的同名函數。這時子類中只是重定義了一個自己的函數virtual void f(),而並沒有override父類中對應的虛函數。
p2是一個指向Base類型的指針,根據虛函數的特性,對p2→f();的處理取決於是否override了父類的虛函數,如果沒有,仍然會調用調用父類中被override的虛函數,但是現在父類中的函數已經成為了virtual void f(int),因此在執行p2→f()時會由於缺少輸入參數而出現上述錯誤。
為了證明上述論斷,可以在執行p2→f()時傳入參數來判斷:
這時可以通過編譯,執行結果為
可以看到,子類自己定義的virtual void f()其實是父類的virtual void f(int)的一個重定義的函數,這時儘管p2實際指向了一個Derived對象,但由於沒有override父類對應的虛函數,在執行 p2→f(2)時將執行父類的virtual void f(int)。
也可以這樣修改:
因為父類中定義了可被子類override的函數,所以這時執行p2→f()又會重新執行子類的virtual void f():
我們甚至還可以這樣驗證:
這時輸出仍然為
這說明如果通過指向父類的指針,調用虛函數時,如果子類重定義了該虛函數(參數列表發生變化),則實際調用的仍是父類中的虛函數。
上面都是通過指向父類的指針來調用虛函數的,那麼如果通過指向子類的指針調用虛函數會發生什麼:
這時輸出就變為了:
這說明,如果通過指向子類的指針調用虛函數,並且子類重定義了父類的虛函數,這時實際調用的就將是子類中的虛函數。