函数重载是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);
显然第二个命名方式是不如第一个的。
在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语言为什么不知此重载。
将一个f函数注释掉后反汇编:
可以看到,f函数是没有经过任何修饰的。所以每个f函数名字都是一样的,编译器无法区分函数,所以无法构成重载。
Windows下反汇编的函数名字不太好看,但还是可以通过一些方法看到。
如上图,修饰过的函数名有些复杂,不利于分析,关于Windows下的函数名修饰规则可以在搜索引擎上看一下。