C++之函數重載
函數重載
函數重載構成條件
函數重載是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下的函數名修飾規則可以在搜索引擎上看一下。