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