函式過載
函式過載構成條件
函式過載是C++允許在同一個作用域中宣告幾個名字相同功能相似的函式,函式過載常被用於處理功能類似、資料型別不同的問題。
高階語言在設計時就有一條原則,語言不能存在二義性,C++為了保證語言不存在二義性對函式過載進行約束:
要構成過載函式必須滿足以下幾個條件之一:
- 形參型別不同
- 形參個數不同
- 形參順序不同
通過程式碼實現一個過載函式,以下程式碼位於三個檔案:
test.cpp
#include"func.h"
int main()
{
f(1, 1.1);
return 0;
}
func.h
#include<stdio.h>
#include<stdlib.h>
void f(int a, float b);
void f(float a, int b);
func.cpp
#include"func.h"
void f(int a, float b)
{
printf("f(int a, float b)\n");
}
void f(float a, int b)
{
printf("f(float a, int b)\n");
}
以上函式構成過載。上面函式構成過載滿足了函式形參順序不同的規則。執行結果為:
從程式執行結果來看,我們不需要指定函式來執行,程式本身根據所傳實參型別來聯絡實際場景判斷用哪個函式,這種方式極大的方便程式設計師。
無法構成過載的特例
值得注意的是,函式的返回型別不同是無法構成函式過載的如:
void f(int a, float b)
{
printf("f(1)\n");
}
int f(int a, float b)
{
printf("f(2)\n");
}
還有無法構成過載的特例:
void f(a = 1)
{
printf("f(a = 1)\n");
}
void f()
{
printf("f()\n");
}
上面這種情況若是呼叫時有實參,編譯器可以判斷為有形參的函式執行,若無實參呼叫f();
,由於上面函式有預設引數,編譯器無法判斷呼叫的是哪個函式,程式也就無法執行。
!!!注意 main函式無法過載!!!
不建議使用函式過載的場景
函式過載對於程式設計師使用起來確實方便,但在有些場景不建議使用函式過載。函式過載使用時最好應用於功能相似的函式。有些時候給函式起不同的名字有利於程式設計師理解函式的功能。舉個例子以下為幾個負責移動螢幕游標的函式:
void moveHome();
void moveAbs(int, int);
void moveRel(int, int, string direction);
將以上幾個函式名字起為move固然也可以,但是這幾個函式構成過載之後函式名就失去了本來擁有的資訊:
void move();
void move(int, int);
void move(int, int, string direction);
顯然第二個命名方式是不如第一個的。
C語言無法構成過載
在vs2019環境中將上面三個檔案中的.cpp字尾全改為.c字尾,編譯程式後程式報錯
由此可以看出C語言不支援函式過載。
那麼為什麼C語不支援函式過載而C++支援呢?
函式名字修飾
從程式碼到程式經過預處理,編譯,彙編,連結幾個過程,C++為了支援函式過載在編譯階段對函式名做了修飾,即名字修飾。由於Windows系統下名字修飾較為複雜,後面的名字修飾演示全為Linux系統下的演示。
在Linux系統下將test.cpp檔案進行彙編後反彙編檢視:
對比兩個函式修飾過的名字和諮詢大佬後知道Linux系統下函式名修飾規則:
_Z+函式名長度+函式名+形參型別
形參型別:
i int
f float
c char
pi *int
其它照此類推
由此可以看出,被以上的3個條件約束的函式過載時修飾後的名字是不會重複的,如此才能構成函式過載
C語言反彙編
我們可以通過此方法來看一下C語言的名字修飾情況,揭祕以下C語言為什麼不知此過載。
將一個f函式註釋掉後反彙編:
可以看到,f函式是沒有經過任何修飾的。所以每個f函式名字都是一樣的,編譯器無法區分函式,所以無法構成過載。
Windows下反彙編的函式名字不太好看,但還是可以通過一些方法看到。
如上圖,修飾過的函式名有些複雜,不利於分析,關於Windows下的函式名修飾規則可以在搜尋引擎上看一下。