引言

实例一

读者也许使用过一些图形库进行图形绘制或游戏制作,如 自己先想想再把鼠标移过来。使用这些库的时候,我们直接调用里面的函数(API)即可实现想要的功能。

API : Application Programming Interface,应用程序编程接口

而在调用这些API时,经常会遇到这样的情况:要使用的函数形参有很多。但是,我们往往只需要设置其中的某些参数,或者不知道其他参数设置了有什么用。这时就更希望那些我们没有自定义的参数使用比较合理的默认值。这样就不必输入全部的参数,使用起来也更加简便。

要实现这点,在C++中,就要用到省略参数

实例二

若要求我们实现一个加法器的程序,使它既能进行int相加,也能进行double相加和float相加。C语言中,我们的做法是分别定义下列的加法函数,然后针对不同类型的数据分别调用。

1
2
3
int add_int(int a,int b); 
double add_double(double a,double b);
float add_float(float a,float b);

有没有办法可以只用一个函数add(),既能实现int相加,也能实现doublefloat相加呢?

要实现这点,在C++中,就要用到函数重载

缺省参数

缺省参数的概念

缺省参数是指声明或定义函数时给函数指定一个参数,这个参数就是默认值。调用这个函数时:

  • 如果对应参数的位置传入实参,则使用传入值。
  • 如果对应的位置没有传入实参,则使用默认值。
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
using std::cout;
int Show(int a=666){
return a;
}
int main(){
//传入值,使用传入的值
cout<<Show(999)<<'\n';
//不传入值,使用默认值
cout<<Show()<<'\n';
return 0;
}

分别输出:999666

缺省参数的分类

  1. 全缺省参数:所有形参都设定了默认值。
  2. 半缺省参数:只有部分参数设定了默认值。

半缺省参数的函数,形参列表只能是 非默认-非默认……默认-默认-…… 这样的情况,即默认的参数只能放在非默认的后面。否则会导致结果和自己想的不一样。

注意事项

  1. 缺省参数的默认值必须在使用之前确定。
  2. 非函数声明时有默认的形参必须放在没有默认值的形参的后面。
  3. 缺省参数在声明和定义里面的出现要注意!这点在下面使用例子进行解释。

在之前例子的基础上,我们对它进行一些变化,请判断下面的三个例子是否正确:

  • 声明和定义分开
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
using std::cout;
int Show(int a=666);
int main(){
cout<<Show(999)<<'\n';
cout<<Show()<<'\n';
return 0;
}
int Show(int a){
return a;
}
结果

这是对的,默认参数要放在声明的地方!

  • 默认参数放在定义处
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
using std::cout;
int Show(int a);
int main(){
cout<<Show(999)<<'\n';
cout<<Show()<<'\n';
return 0;
}
int Show(int a=666){
return a;
}
结果

编译器报错:too few arguments to function ‘int Show(int)’。即将默认值放在定义处时,由于编译器先解析的是函数声明,运行到相应函数的时候会到声明处去查询,所以不知道这个函数有默认值。(解析顺序的原因)

  • 默认参数放在声明和定义处同时出现
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
using std::cout;
int Show(int a=666);
int main(){
cout<<Show(999)<<'\n';
cout<<Show()<<'\n';
return 0;
}
int Show(int a=666){
return a;
}
结果

编译器报错:为‘int Show(int)’的第 1 个形参指定了默认实参,即不能重复设定默认实参。

总结

当声明和定义分开时,默认参数必须放在声明处,只放在定义处,或声明和定义处同时设置默认参数都是错误的!

函数重载

引言问题解决

回到引言中加法器的问题,我们如何使得add()函数既能实现int相加,也能实现doublefloat相加呢?我们的方法是:使用函数重载,定义一系列同名函数。

1
2
3
4
5
6
7
8
9
10
//add.c中的内容
int add(int a,int b){
return a+b;
}
float add(float a,float b){
return a+b;
}
double add(double a,double b){
return a+b;
}

如果对多文件编程有疑惑,可以去简单看看这篇文章:

函数重载的概念

C++允许在同一作用域中声明几个功能类似的同名函数。这些函数的形参列表必须不同(想想可以有哪些不同)。它常用于简化处理一系列功能类似,但数据类型不同的函数问题。

注意事项

同名函数的形参列表一定要不同!

因为我们调用函数的时候是通过函数名(实参列表)这样的方式进行的,这些函数的名字都相同,所以参数列表必须不同,这样编译器才能将它们分开,不然就会产生歧义。


函数重载的用法到这里就结束了,下面将利用汇编代码探究函数重载的原理,感兴趣的话可以看看。

作用机理

我们将之前生成的可执行文件用objdump指令进行反汇编,然后找到add()函数的位置。

很容易发现,两个int相加,就有后缀iidoublefloat同理。前缀-Z3代表了返回类型( int , doublefloat 归为一类)。

可想而知,C++依据形参列表给函数名字添加相应的修饰符,从而将同名函数区分开。使用的时候,编译器就会通过前缀和后缀进行筛选,选出此处应该使用的函数。

C++进行函数重载给函数名添加修饰符这一点和区分不同命名空间的成员的作用机理很相似。

C语言不支持函数重载,就是因为没有给函数添加修饰符这个机制。