函数的重载使得 C++ 程序员对完成类似功能的不同函数可以统一命名,减少了命名所花的心思。例如,可能会需要一个求两个整数的最大值的函数,也可能还要写一个求三个实数的最大值的函数,这两个函数的功能都是求最大值,那么就都命名为 Max 即可,不需要一个命名为 MaxOfTwoIntegers,另一个命名为 MaxOfThreeFloats。
在调用同名函数时,编译器怎么知道到底调用的是哪个函数呢?编译器是根据函数调用语句中实参的个数和类型来判断应该调用哪个函数的。因为重载函数的参数表不同,而调用函数的语句给出的实参必须和参数表中的形参个数和类型都匹配,因此编译器才能够判断出到底应该调用哪个函数。例如下面的程序:
#include <iostream> using namespace std; void Max(int a, int b) { cout << "Max 1" << endl; } void Max(double a, double b) { cout << "Max 2" << endl; } void Max(double a, double b, double c) { cout << "Max 3" << endl; } int main() { Max(3, 4); //调用 int Max(int, int) Max(2.4, 6.0); //调用 double Max(doubleA double) Max(1.2, 3.4, 5); //调用 double Max(double, double, double) Max(1, 2, 3); //调用 double Max(double, double, double) Max(3, 1.5); //编译出错:二义性 return 0; }
以上程序如果去掉第 21 行编译出错的语句,输出结果是:
Max 1
Max 2
Max 3
Max 3
显然,编译器根据调用 Max 函数的语句所给的实参的个数和类型,可以找到完全匹配的函数。例如第 17 行,实参是两个整型数,那么调用的当然就应该是原型为 void Max(int, int) 的那个 Max 函数。
第 21 行编译会出错,因为两个实参一个是整型,一个是实数型。如果将整型自动转换成 实数型,那么看来应该调用 void Max(double, double) 这个函数;可是如果将实数型去尾自动转换为整型,那么调用 void Max(int, int) 似乎也说得过去。
C++ 设计者认为,此时编译器应该因不知如何选择而报告二义性的错误,而不是规定优先选择其中某一种。因为如果硬性规定的话,程序员很可能记不清到底编译器是怎么规定的,从而可能导致程序员心里认为应该调用这个 Max 函数,实际上编译器的处理是调用另一个 Max 函数,结果程序员因编译器不会报错而无法察觉这个问题。好的工具总是应该让使用者少犯错误,或者犯了错误也能马上发现。
如果去掉 void Max(int, int) 函数或者 void Max(double, double) 函数中的任何一个,则第 21 行就不会导致编译时的二义性错误了,因为此时实参该如何自动转换才能和 Max 函数匹配是确定的。
在两个函数同名而参数个数不同,但是其中参数多的那个函数的参数又可以取默认值的情况下,也可能会引发二义性。例如下面两个函数:
int Sum(int a, int b, int c = 0); int Sum(int a, int b);
则函数调用语句:
Sum (1, 2);
就会在编译时导致二义性错误。因为编译器不知道是应该以 (1, 2, 0) 作为参数调用第一个 Sum 函数,还是以 (1, 2) 作为参数调用第二个 Sum 函数。同样,将编译器设计成在这种情况下优先选择某一种也是不合理的。
需要强调一点,同名函数只有参数表不同才能算重载。两个同名函数的参数表相同而返回值类型不同不是重载,而是重复定义,是不允许的。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/21504.html