過載的奧義之函數過載

2023-01-08 12:00:21

一、基本定義        

        過載,顧名思義從字面上理解就是重複裝載,打一個不恰當的比方,你可以用一個籃子裝蔬菜,也可以裝水果或者其它,使用的是同一個籃子,但是可以用籃子重複裝載的東西不一樣。

        函數過載是C++多型(靜態多型)的特徵體現,它可以允許重複使用同一個函數名(籃子)的函數,但是函數的參數列(籃子裝的東西)是可以不一樣的。這樣就可以利用函數的過載功能設計一系列功能相近,但是功能細節不一樣的函數介面。


二、應用舉例        

        以同一個函數printData為例:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 void printData(const char *str, int num)
 5 {
 6   //函數體;
 7 }
 8 
 9 void printData(const char *str)
10 {
11   //函數體;
12 }
13 
14 void printData(double data, int num)
15 {
16   //函數體;
17 }
18 
19 void printData(int data, int num)
20 {
21   //函數體;
22 }
23 
24 void printData(long data, char num)
25 {
26   //函數體;
27 }
28 
29 class Test
30 {
31   public:
32          void MyPrint(int num) {cout << "class int: " << num << endl;}
33          void MyPrint(float num) {cout << "class float: " << num << endl;}
34          void MyPrint(char num) {cout << "class char: " << num << endl;}
35 };
36 
37 int main(void)
38 {
39   printData("hello", 5); // (const char *str, int num)
40   printData("hello"); // (const char *str)
41   printData(1993.0, 97);
42   printData(1993, 98);
43   printData(1993L, 99);
44   Test test1;
45   test1.MyPrint(2); // class int: 2
46   test1.MyPrint(2.0f); // class float: 2.0 浮點型必須要顯式型別,否則編譯器不知道該轉換為int還是float。
47   test1.MyPrint("hello"); // class char: hello
48   return 0;
49 }

  使用過載函數時,需要在函數呼叫中使用與對應的過載函數匹配的函數引數型別。

        而如下:

1 unsigned int para = 4321;
2 printData(4321, 5);

        此時的printData呼叫和哪個原型匹配呢?答案它不與任何函數原型匹配,而沒有匹配的原型不會停止呼叫其中某一個函數,C++會嘗試用標準的強制型別轉換與之匹配,比如使用 printData(double data, int num),就可以將para的型別強制轉換為double型別。但是還有printData(int data, int num)和printData(long data, char num)這兩個函數可以強制轉換para。因此,C++將拒絕這種函數的呼叫,將這種呼叫視為錯誤。

        過載函數通常用在同一個作用域內,用同一個函數名命名一組功能相似的函數,這樣做減少了函數名的數量,提高了函數的通用性,避免了名稱空間的汙染,對於程式的可讀性有很大的好處。


三、非函數過載的情況

        下面這種兩種情況不能視為函數過載:

1 int fun(int a);
2 int fun(int &a);

        從編譯器的角度出發,引數a與參數列原型int a和int &a都匹配,編譯器無法確定使用哪個函數,為避免這種混亂,編譯器在檢查引數型別時將把型別本身和型別參照看作是同一個特徵型別。

1 int fun(int a, float b);
2 double fun(int a, float b);

        C++不允許這樣的方式過載函數,雖然返回值可以不一樣,但是參數列必須不一樣。


四、函數過載的使用原則

        (1)、僅當函數的基本功能比較相近,但是需要使用不同形式的引數實現功能時才應該使用函數過載,儘量不要用同一函數名去實現完全不相干的功能;

        (2)、在同一個作用範圍內使用函數過載,同一個範圍即:同一個名稱空間或者同一個類等;

        (3)、過載函數的名稱必須相同,函數的參數列須不相同,即參數列中引數的型別,引數的個數或引數的順序不相同;

        (4)、過載函數可以有相同的返回值型別或者不同的返回值型別,反之僅僅是返回型別不同不足以作為函數的過載。


五、FAQ

1、C++中對函數過載是如何處理的?

        在.cpp檔案中,雖然兩個函數的函數名一樣,但是,C++編譯器在內部使用「名稱修飾」或「名稱矯正」轉換,它根據函數中參數列的區別為每個函數進行加密 ,例如:

        int fun(int a, float b)和double fun(int a, float b)

        編譯器在內部可以轉換為:

        ?fun@@YAHHH@Z和?fun@@YAMMM@Z

         "?"表示名稱開始,"?"後邊是函數名;「@@YA」表示參數列開始,後邊的3個字元分別表示返回值型別,兩個引數型別;「@Z」表示名稱結束。

        由於在.cpp檔案中,兩個函數生成的符號表中字元的名稱不一樣,所以是可以編譯通過的。

2、C語言中為什麼不能支援函數過載?

        編譯器在編譯.c檔案時,只會給函數進行簡單的重新命名。具體的方法是給函數名之前加上"_」;所以編譯前兩個函數名相同的函數在編譯之後的函數名也照樣相同;因此呼叫時會因為不知道到底呼叫哪個而出錯。

        int fun(int a, float b)和double fun(int a, float b)

        編譯器在內部都轉換為:_fun,無法區分,

        只有不同的函數名字int fun1(int a, float b)和double fun2(int a, float b)

        編譯器在內部轉換為:_fun1和_fun2,這才能區分開來。


 更多技術內容和書籍資料獲取敬請關注微信公眾號「明解嵌入式」