C++中的虛函數重載

2021-02-21 小米開放平臺

 林洋

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():

我們甚至還可以這樣驗證:

這時輸出仍然為

這說明如果通過指向父類的指針,調用虛函數時,如果子類重定義了該虛函數(參數列表發生變化),則實際調用的仍是父類中的虛函數。
上面都是通過指向父類的指針來調用虛函數的,那麼如果通過指向子類的指針調用虛函數會發生什麼:

這時輸出就變為了:

這說明,如果通過指向子類的指針調用虛函數,並且子類重定義了父類的虛函數,這時實際調用的就將是子類中的虛函數。

相關焦點

  • c++之重載函數學習總結
    一、C++中的函數重載:1、函數重載的概念:用同一個函數名定義不同的函數當函數名和不同的參數搭配時函數的含義不同注意:在c語言中是沒有函數重載這個概念的。6、小結:函數重載是c++中引入的概念函數重載的本質是相互獨立的不同函數
  • c++ 內存,虛函數,運算函數,三角函數
    :就是java中的抽象,純虛函數只有聲明沒有具體實現就是空方法,在子類中必須重新寫,虛函數就是在基類中寫了有實現。他們都得用關鍵字 virtual(虛擬的) 聲明的函數1. 虛函數和純虛函數可以定義在同一個類(class)中,含有純虛函數的類被稱為抽象類(abstract class),而只含有虛函數的類(class)不能被稱為抽象類(abstract class)。2.
  • C++語言虛函數實現多態的原理
    自上一個帖子之間跳過了一篇總結性的帖子,之後再發,今天主要研究了c++語言當中虛函數對多態的實現,感嘆於c++設計者的精妙絕倫C++中虛函數表的作用主要是實現了多態的機制。首先先解釋一下多態的概念,多態是C++的特點之一,關於多態,簡而言之就是 用父類的指針指向其子類的實例,然後通過父類的指針調用實際子類的成員函數。
  • C++ | 虛函數簡介
    緣起 在上一篇文章中,測試代碼2 中的 pBaseA->AA(); 輸出的內容很「奇怪」。其實,完全在情理之中。本文將簡單探究一下 c++ 中的虛函數實現機制。本文主要基於 vs2013 生成的 32 位代碼進行研究,相信其它編譯器(比如,gcc)的實現大同小異。
  • C++虛函數詳解
    這樣,在有虛函數的類的實例中這個表被分配在了這個實例的內存中,所以,當我們用父類的指針來操作一個子類的時候,這張虛函數表就顯得由為重要了,它就像一個地圖一樣,指明了實際所應該調用的函數。這裡我們著重看一下這張虛函數表。C++的編譯器應該是保證虛函數表的指針存在於對象實例中最前面的位置(這是為了保證取到虛函數表的有最高的性能——如果有多層繼承或是多重繼承的情況下)。
  • 【C++札記】深入淺出C++函數重載
    學過C語言的同學應該很清楚,在C語言中,同一個程序中是不能定義多個名稱相同的函數,否則編譯會報重定義的錯誤信息,但是C++中則允許定義多個名稱相同的函數,在C++中,這稱之為函數重載,讓我們來看看更官方一點的定義,函數重載是指在同一作用域內,可以有一組具有相同函數名,不同參數列表的函數,這組函數被稱為重載函數。
  • C++中的虛函數(virtual function)
    這完全歸功於多態--編譯器針對虛函數產生了可以在運行時刻確定被調用函數的代碼。1.3 如何「動態聯編」 編譯器是如何針對虛函數產生可以再運行時刻確定被調用函數的代碼呢?也就是說,虛函數實際上是如何被編譯器處理的呢?Lippman在深度探索C++對象模型[1]中的不同章節講到了幾種方式,這裡把「標準的」方式簡單介紹一下。
  • 每日一問(11) 什麼是虛函數
    別人都知道,我不知道 才是最尷尬的地方C++通過指針實現了多態,運行時函數重載決議,是他最有優秀地方,但是也是最讓人痛苦地方,內存模型假設存在讓對象生命周期管理更加複雜。(牛客)思考:複製操作和虛函數有關係嗎?
  • 詳解C++ 的隱式類型轉換與函數重載!
    作者 | 櫻雨樓責編 | 屠敏在上篇中,我們討論了C++中與隱式類型轉換相關的一些話題,而函數重載是與隱式類型轉換相關的又一大重要話題,本篇將要討論的內容即為隱式類型轉換與函數重載之間的相關話題。隱式類型轉換與重載確定C++中,如果同時定義了多個函數名稱相同,但函數籤名不同的函數,則此行為稱為函數重載。調用重載函數時,編譯器將根據調用的參數數量與類型確定被調用的是哪一個函數,此過程稱為重載確定。
  • C++ 虛函數表及多態內部原理詳解
    2、 虛表可以繼承,如果子類沒有重寫虛函數,那么子類虛表中仍然會有該函數的地址,只不過這個地址指向的是基類的虛函數實現。如果基類3個虛函數,那麼基類的虛表中就有三項(虛函數地址),派生類也會有虛表,至少有三項,如果重寫了相應的虛函數,那麼虛表中的地址就會改變,指向自身的虛函數實現。如果派生類有自己的虛函數,那麼虛表中就會添加該項。
  • C/C++編程筆記:運算符重載丨重載C++中的New和Delete運算符
    (1)如果使用某個類的成員函數來重載這些運算符,則意味著這些運算符僅針對該特定類才被重載。(2)如果重載是在類外部完成的(即它不是類的成員函數),則只要您使用這些運算符(在類內或類外),都將調用重載的「 new」和「 delete」。這是全局超載。
  • C++中操作符也能重載,有意思
    ,即可調用不同的函數實現,既然函數能夠重載,C++編譯器也可以支持運算符重載,其實就在於編譯器如何解析處理罷了。運算符重載也是面向對象的一種需求,在程序中運算符是一種建立對象關係的方法,運算符的重載能夠幫助對象實現更多靈活的關係處理和運算。比如對象的加法、減法運算,默認情況下就不支持,但可通過運算符重載就可以建立此關係,說白了C++中的運算符不再是簡單的數據運算,更多的像是一種函數的封裝處理。
  • 面試必知必會|理解C++虛函數
    綁定體現了函數調用和函數本身代碼的關聯,也就是產生調用時如何找到提供調用的方法入口,這裡又引申出兩個概念:在C++泛型編程中可以基於模板template和重載override兩種形式來實現靜態多態。動態多態主要依賴於虛函數機制來實現,不同的編譯器對虛函數機制的實現也有一些差異,本文主要介紹Linux環境下gcc/g++編譯器的實現方法。
  • 函數重載
    查了一些資料了解到,cpp文件在編譯時會將函數的參數列表一起編譯成新的函數名,究其原因,就是今天要討論的問題:函數重載。        函數重載:C++允許在同一範圍中聲明幾個功能類似的同名函數,但是這些同名函數的形式參數(指參數的個數、類型或者順序)必須不同,也就是說用同一個函數完成不同的功能(以上摘自百度百科)。
  • python3使用ctypes在windows中訪問C和C++動態連結庫函數示例
    python3使用ctypes在windows中訪問C和C++動態連結庫函數示例這是我們的第一個示例,我們儘量簡單,不傳參,不返回,不訪問其他的動態連結庫一 測試環境介紹和準備c語言函數,如果你源碼是.c也就是用c語言編譯則不需要加extern "C"。
  • c++運算符重載
    算符重載,就是對已有的運算符重新進行定義,賦予其另一種功能,以適應不同的數據類型。<返回類型說明符>  operator  <運算符符號>  (<參數表>) {      <函數體>  }重載運算符的方式有哪幾種?
  • C++虛函數表剖析
    三、虛表指針虛表是屬於類的,而不是屬於某個具體的對象,一個類只需要一個虛表即可。同一個類的所有對象都使用同一個虛表。 為了指定對象的虛表,對象內部包含一個虛表的指針,來指向自己所使用的虛表。為了讓每個包含虛表的類的對象都擁有一個虛表指針,編譯器在類中添加了一個指針,*__vptr,用來指向虛表。
  • C++ 類、繼承、重載《Rice C++ 學習開發》
    因此基類成員如果不想被派生類的成員函數訪問,則應在基類中聲明為 private。:C++ 允許在同一作用域中的某個函數和運算符指定多個定義,分別稱為函數重載和運算符重載。重載聲明是指一個與之前已經在該作用域內聲明過的函數或方法具有相同名稱的聲明,但是它們的參數列表和定義(實現)不相同。當您調用一個重載函數或重載運算符時,編譯器通過把您所使用的參數類型與定義中的參數類型進行比較,決定選用最合適的定義。選擇最合適的重載函數或重載運算符的過程,稱為重載決策。
  • C++中虛函數和純虛函數的區別與總結
    return 0;  } 這個例子是虛函數的一個典型應用,通過這個例子,也許你就對虛函數有了一些概念。它虛就虛在所謂「推遲聯編」或者「動態聯編」上,一個類函數的調用並不是在編譯時刻被確定的,而是在運行時刻被確定的。由於編寫代碼的時候並不能確定被調用的是基類的函數還是哪個派生類的函數,所以被成為「虛」函數。
  • 詳細介紹C語言中重載函數的應用
    今天給大家介紹重載函數。  重載函數(overloaded function)是C++支持的一種特殊函數,C++編譯器對函數重載的判斷更是C++語言中最複雜的內容之一  首先我們先明確一下重載函數的定義:在相同的聲明域中的函數名相同的,而參數表不同的,即通過函數的參數表而唯一標識並且來區分函數的一種特殊的函數。  您也許要問,函數為什麼要重載呢?