函數重載(overload)和函數重寫(override)


1. 前言:

  在C++中有兩個非常容易混淆的概念,分別是函數重載(overload)和函數重寫(overwirte)。雖然只相差一個字,但是它們兩者之間的差別還是非常巨大的。

  而通過深入了解這兩個概念的區別,會對C++的面向對象機制有一個更深入的理解。

2 函數重載(overload function)

2.1 函數重載的概念:

2.1.1 概念:

當函數具有相同的名稱,但是參數列表不相同的情形(包括參數的個數不同或參數的類型不同),這樣的同名而不同參數的函數之間,互相被稱之為重載函數。

2.1.2 基本條件:

  • 函數名必須相同;
  • 函數參數必須不相同,可以是參數類型或者參數個數不同;
  • 函數返回值可以相同,也可以不相同。(備注:但是如果函數的名稱和參數完全相同,僅僅是返回值類型不同,是無法進行函數重載的。

2.1.3 注意:

  • 只能通過不同的參數樣式進行重載,例如:不同的參數類型,不同的參數個數,或者不同的參數順序
  • 不能通過訪問權限、返回類型、拋出的異常不同而進行重載;
  • 重載的函數應該在相同的作用域下。

2.1.4 函數重載實例判斷:

以下的集中寫法,分別表示了哪些是重載的,哪些不是重載的。 
(1) void func1( int arg1); 
(2) void func1( double arg1); 
(3) void func1( int arg1, int arg2); 
(4) bool func1(int arg1, double arg2) 
(5) int func1(int arg1);

在上述的5個函數中,函數名稱都是func1,完全相同;但是: 
(2)與(1)的參數個數相同,參數類型不同,構成重載; 
(3)與(1)和(2)的參數個數不同,構成重載; 
(4)與(1)和(2)的參數個數不同,與(3)的參數個數相同,但是第二個參數類型不同,構成重載; 
(5)與(1)的參數個數和參數類型均相同,僅返回值的類型不相同,不構成重載;但是(5)與(2),(3)和(4)除返回值不同外,均有參數類型或參數個數不同的情況,因此構成重載關系。

 

2.2 函數重載的應用

讀者可能會問,既然函數重載這個概念這么拗口,而且有時候又容易和函數重寫概念弄混而導致出錯,那么為什么在C++里面要有這么一個概念出現呢?

原因其實也很簡單,就是因為在一個程序中,會出現很多很多,完成的函數功能完全相同,而僅僅是函數的參數略有不同的情形。這時如果沒有函數重載這個概念,那么開發人員恐怕就要為如何為功能完全相同的函數起不同的名而頭疼了。

 

在各種開源的庫中,我們也經常可以看到函數重載的身影。比如:

(1)類的構造函數,通常就是函數重載的典型應用。因為一個類通常是可以有很多種構造方式的。 
如QT里面的QString類的構造函數,提供了9種不同的構造函數,這9種構造函數的函數名完全相同,但是它們的參數類型或參數個數卻不完全相同,因此是合法的。如圖所示: 

(2)類的成員函數,如賦值函數等。 
如VTK的vtkImageData類的兩個成員函數就是重載的。如: 

 

 

這兩個成員函數的函數名稱都是SetDimensions(),但是第一個函數的參數是3個int型的值;另一個函數的參數是一個const int型的數組,返回值都是void。這樣也是可以構成函數重載的。

在安裝有編程助手的情況下編寫代碼時,如果遇到一個類的成員函數有重載時,助手通常會提示開發者,要選擇哪一個重載函數。如下圖所示。vtkImageData的SetDimensions()函數有兩個重載形式,因此在編寫代碼時,助手會提示2 of 2,表示這是2個重載函數中的第二個,點擊可以切換到第一個重載函數。開發者需要根據上下文的要求,來選擇相應的重載函數進行編寫。

 


 

 

3 函數重寫(override function)

雖然與函數重載僅僅只有一個字的差別,但是這兩個概念卻是相差了很遠很遠。它倆似乎一點關系都沒有。也正因為如此,這個非常考驗C++語言的基本功,也是歷年C++筆試中經常會出現的考題。

3.1 函數重寫(override function)

3.1.1 概念:

函數重寫,也被稱為覆蓋,是指類重新定義父類中有相同名稱和參數的虛函數,主要在繼承關系中出現

3.1.2 基本條件:

  • 重寫的函數和被重寫的函數必須都為virtual函數,並分別位於基類和派生類中;
  • 重寫的函數和被重寫的函數,函數名和函數參數必須完全一致
  • 重寫的函數和被重寫的函數,返回值相同,或者返回指針或引用,並且派生類虛函數返回的指針或引用的類型基類中被替換的虛函數返回的指針或引用的類型或者其子類型(派生類型)。

3.2 函數重寫的應用

有個規則是賦值兼容性原則,而這個規則有bug,當子類和父類的函數重名的時候,不管是基類的對象去指向/引用子類對象,都是調用基類的函數,而和子類無關,這明顯是一個bug,

所以,只能用函數重寫(也就是引出了多態),這樣就可以區分和調用子類和父類里重名的函數了。[這里不能用函數重載是因為這里發生在兩個類里,而重載不僅僅是在一個類里進行區分]

 PS: 重寫/多態是類的特性,而重載不是,在全局函數里也可以用重載
 

今天在工作的時候,就是因為在重寫基類的某一個虛函數時,由於在復制時把函數的參數類型和基類的參數類型搞得不一致了,導致重寫失敗。

因此,在調試代碼時,本以為程序會進入派生類的重寫后的函數中,但是實際卻一直進入基類的函數中。最后在網上查詢原因時,才恍然大悟,原來是由於自己的失誤,而導致了重寫失敗。

具體是: 
Dx3DActorRotationPanOplayer : public DxBaseOplayer. 
在基類DxBaseOplayer中有一系列的關於響應鼠標事件的虛函數: 

 

其中,在派生類中我想重新實現其中的一個虛函數 OnMouseLeave()。 
但是,我在子類定義該函數時,卻寫成了: 

 

表面上看起來似乎很像。但是仔細一看,函數的參數是不相同的。 

基類的第一個參數類型是:QEvent, 而派生類的第一個參數類型是:QMouseEvent。

正是由於這個參數類型的不同,而導致了派生類實際並沒有重寫基類的這個成員函數

 

因此,在基類的指針調用這個函數時,便無法調用到子類的這個重寫函數了。 
只要把派生類的第一個參數類型也修改為QEvent,那么便實現了函數的重寫了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM