重载函数
在平时生活中经常会出现这样一种情况,一个班里同时可能同时有两个叫小明的同学,甚至有多个,但是他们的身高、体重、性别、外貌等会有所不同,老师点名字时都会根据他们的特征来区分,如高个小明,胖小明等。其实在编程语言里也会存在这种情况,几个不同的函数有着相同的名字,在调用时根据参数的不同确定调用哪个函数,这就是C++提供的函数重载机制。
所谓重载(overload)函数就是在同一个作用域内几个函数名字相同但形参列表不同。例如在同一个作用域内同时定义几个add()函数。
void add(int x, int y);
void add(float x);
double add(double x, double y);
这几个就是重载函数,它们函数名相同但参数列表却不相同,参数列表的不同有三种含义:参数个数不同,或者参数类型不同或者参数个数和类型都不同。
为了加深读者的理解,我们用一个案例来调用这几个重载函数,如例1所示。
例1
1 #include <iostream>
2 using namespace std;
3 void add(int x, int y)
4 {
5 cout << "int: " << x + y << endl;
6 }
7 void add(float x)
8 {
9 cout << "float: " << 10 + x << endl;
10 }
11 double add(double x, double y)
12 {
13 return x + y;
14 }
15 int main()
16 {
17 add(10.2); //一个实型参数
18 add(1, 3); //两个整型参数
19 cout << add(3.2, 2.3) << endl; //两个实型参数
20 system("pause");
21 return 0;
22 }
运行结果如图1所示。
图1 例1运行结果
从图1可以看出,三次调用add()函数,传入不同的参数就调用了对应的函数,在这个过程中,编译器会根据传入的实参与几个重载函数逐一匹配,然后根据比较结果决定到底调用哪个函数。
注意:重载函数不能以返回值来区分不同。
当重载函数中的形参都是普通形参,定义和调用一般不会出现问题,但是当形参由const修饰或者有默认参数值时,有一些问题需要注意,接下来我们就分别来学习一下这两种情况。
1、重载与const形参
如果是底层的const形参,即const修饰的是指针指向的变量,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载,例如下面两对函数:
void func1(int *x); //普通指针
void func1(const int*x); //常量指针
void func2(int &x); //普通引用
void func2(const int &x); //常引用
上面的两对重载函数,编译器可以通过实参是否是常量来推断应该调用哪个函数。对于const对象,因为const对象不能转换成其他类型,所以只能把const对象传递给const形参;而对于非const对象,因为非const对象可以传递给const形参,所以上面函数都可以被非const对象调用,但是在重载函数中,编译器会优先选择非常量版本的函数。
接下来通过一个案例来加深读者对重载函数中const形参的印象,如例2所示。
例2
1 #include <iostream>
2 using namespace std;
3 void func1(const int *x) //常量指针
4 {
5 cout << "const int*: " << *x << endl;
6 }
7 void func1(int *x) //普通指针
8 {
9 cout << "int*: " << *x << endl;
10 }
11 void func2(const int &x) //常引用
12 {
13 cout << "const int&: " << x << endl;
14 }
15 void func2(int &x) //普通引用
16 {
17 cout << "int&: " << x << endl;
18 }
19 int main()
20 {
21 const int a = 1;
22 int b = 2;
23 func1(&a); //常量参数
24 func1(&b); //非常量参数
25
26 func2(a); //常量参数
27 func2(b); //非常量参数
28 system("pause");
29 return 0;
30 }
运行结果如图2所示。
图2 例2运行结果
由图2可以看出,当传入常量对象参数时会调用const形参函数,当传入非常量对象时,则优先调用了非const形参函数。
但如果是顶层的const形参,即const修饰的是指针本身,则无法和另一个没有顶层const的形参区分开来,对于const修饰形参变量,则只是不能在函数内部改变变量的值,并不一定要求传入进来的变量就得是常量;对于const修饰指针,只是保证这个指针不指向别的变量而已,而不要求它指向的变量是常量。例如下面的函数:
void func1(int x);
void func1(const int x); //函数func1()重复声明
void func2(int *x);
void func2(int * const x); //函数func2()重复声明
这两对函数,第二个函数都是对第一个函数的重复声明,因为顶层const不影响传入函数的对象,所以无法以顶层const形参来定义重载函数。
2、重载和默认参数
当使用具有默认参数的函数重载形式时须注意防止调用的二义性,例如下面的两个函数:
int add(int x, int y = 1);
void add(int x);
当有如下函数调用时就会产生歧义:add(10);它既可以调用第一个add()函数也可以调用第二个add()函数,编译器无法确认到底要调用哪个重载函数,这就产生了调用的二义性。在使用时要注意这种情况的发生。