過載的奧義之函式過載

Sharemaker發表於2023-01-08

一、基本定義        

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

        函式過載是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,這才能區分開來。


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

相關文章