重载和默认参数
在 C 语言里,两个函数不能同名。C++ 可以:只要参数不一样,几个函数就能共用一个名字,这叫重载。调用时,编译器看你给的实参,挑出合适的那一个。
1#include <iostream>2 3int bigger(int a, int b) {4 if (a > b) {5 return a;6 }7 return b;8}9 10double bigger(double a, double b) {11 if (a > b) {12 return a;13 }14 return b;15}16 17int main() {18 std::cout << bigger(3, 8) << '\n';19 std::cout << bigger(2.5, 1.5) << '\n';20 return 0;21}
8 2.5
不用起 bigger_int、bigger_double 这样的名字,用的人只要记住「bigger 取大的那个」。
同名,参数不同
参数不同,可以是个数不同,也可以是类型不同:
1#include <iostream>2 3int area(int side) {4 return side * side;5}6 7int area(int width, int height) {8 return width * height;9}10 11double area(double radius) {12 return 3.14159 * radius * radius;13}14 15int main() {16 std::cout << "正方形:" << area(4) << '\n';17 std::cout << "长方形:" << area(4, 6) << '\n';18 std::cout << "圆:" << area(2.0) << '\n';19 return 0;20}
正方形:16 长方形:24 圆:12.5664
三个 area:一个参数的 int、两个参数的 int、一个参数的 double。area(4) 和 area(2.0) 都只给了一个实参,靠类型分开:4 是 int,2.0 是 double。
编译器怎么挑
编译器先找类型完全一样的。找不到,再看哪个能「升一级」就合适,比如 char 和 bool 可以当 int 用。还不行,才考虑别的转换。
1#include <iostream>2 3void show(int n) {4 std::cout << "int:" << n << '\n';5}6 7void show(double d) {8 std::cout << "double:" << d << '\n';9}10 11int main() {12 show(7);13 show(7.5);14 show('A');15 return 0;16}
int:7 double:7.5 int:65
'A' 是 char,两个 show 都不完全一样;char 升成 int 最自然,所以挑了 show(int),显示的是 'A' 的编码 65。
这段程序显示什么?
1#include <iostream>2 3void show(int n) {4 std::cout << "int\n";5}6 7void show(double d) {8 std::cout << "double\n";9}10 11int main() {12 show(10 / 4);13 return 0;14}
10 / 4 是两个 int 相除,结果还是 int(得 2,第 4 课讲过)。类型完全一样的是 show(int)。编译器挑的时候只看类型,不看算出来的值。
只有返回类型不同,不算重载。int read_value() 和 double read_value() 参数一样,调用 read_value() 时编译器没法分辨,所以第二个定义就是编译错误。
挑不出来
有时候两个函数一样合适,编译器不会替你猜,直接报错:
1#include <iostream>2 3double cost(int count, double price) {4 return count * price;5}6 7double cost(double weight, int price) {8 return weight * price;9}10 11int main() {12 std::cout << cost(3, 5) << '\n';13 return 0;14}
main.cpp: In function 'int main()':
main.cpp:12:22: error: call of overloaded 'cost(int, int)' is ambiguous
12 | std::cout << cost(3, 5) << '\n';
| ~~~~^~~~~~call of overloaded 'cost(int, int)' is ambiguous:调用 cost(int, int) 有歧义。第一个 cost 要把 5 转成 double,第二个要把 3 转成 double,各转一个,不分高下。在你自己的电脑上,g++ 还会在下面列出两个候选(candidate)。
改法是把意思写清楚:写 cost(3, 5.0) 就是第一个,写 cost(3.0, 5) 就是第二个。更好的办法是一开始就别让两个重载这么像。
默认参数
有的参数大多数时候都是同一个值。给它一个默认值,调用时不写,就用默认的:
1#include <iostream>2 3double final_price(double price, double discount = 0) {4 return price * (1 - discount);5}6 7int main() {8 std::cout << final_price(200) << '\n';9 std::cout << final_price(200, 0.25) << '\n';10 return 0;11}
200 150
final_price(200) 没给 discount,它就是 0;final_price(200, 0.25) 给了,就用 0.25。
几个参数都可以有默认值。调用时从左往右给,没给的用默认值:
1#include <iostream>2 3void line(int width = 10, char ch = '-') {4 for (int i = 0; i < width; i++) {5 std::cout << ch;6 }7 std::cout << '\n';8}9 10int main() {11 line();12 line(5);13 line(5, '*');14 return 0;15}
---------- ----- *****
line(5) 的 5 给了第一个参数 width,ch 还是 '-'。没法跳过 width 只给 ch。
默认值只能放在后面
一个参数有了默认值,它右边的参数也都得有。下面这样写是编译错误:
1void line(int width = 10, char ch) {2 // ...3}
g++ 会说 default argument missing for parameter 2:第 2 个参数少了默认值。道理和上面一样:实参从左往右对上参数,调用 line('*') 时,'*' 只会给 width,ch 永远拿不到值。所以要把「常常不用给」的参数排在最后。
注意: 先写了声明(原型)的函数,默认值写在声明里,定义里不要再写一遍,否则也是编译错误。
重载和默认参数撞车
默认参数让一个函数能用好几种个数的实参来调用,于是它可能和另一个重载撞上:
1#include <iostream>2 3int total(int a, int b) {4 return a + b;5}6 7int total(int a, int b, int c = 0) {8 return a + b + c;9}10 11int main() {12 std::cout << total(1, 2, 3) << '\n';13 std::cout << total(1, 2) << '\n';14 return 0;15}
main.cpp: In function 'int main()':
main.cpp:13:23: error: call of overloaded 'total(int, int)' is ambiguous
13 | std::cout << total(1, 2) << '\n';
| ~~~~~^~~~~~total(1, 2, 3) 只有第二个能接;total(1, 2) 两个都能接,编译器又挑不出来了。第二个 total 有了默认值,已经能做第一个的事,删掉第一个就好。
能用默认参数解决的,就别再写一个重载。
练一练
写好代码,点检查。提示随便看,实在想不出来再看答案。
两个数或三个数的平均
✓写两个重载的 average:一个接收两个 int,一个接收三个 int,都返回 double 的平均数。程序要显示 7.5 和 6。
7.5 6
1#include <iostream>2 3int main() {4 std::cout << average(5, 10) << '\n';5 std::cout << average(4, 6, 8) << '\n';6 return 0;7}
小费
✓给 tip 函数的 rate 加上默认值 0.15,让 tip(100) 也能用。程序要显示 15 和 20。
15 20
1#include <iostream>2 3double tip(double bill, double rate) {4 return bill * rate;5}6 7int main() {8 std::cout << tip(100) << '\n';9 std::cout << tip(100, 0.2) << '\n';10 return 0;11}
修好它:响几声
✓这段程序编译不过:ring() 不知道该调哪一个。删掉多余的函数,让 ring() 响两声,ring(3) 响三声。
叮叮 叮叮叮
1#include <iostream>2 3void ring() {4 std::cout << "叮\n";5}6 7void ring(int times = 2) {8 for (int i = 0; i < times; i++) {9 std::cout << "叮";10 }11 std::cout << '\n';12}13 14int main() {15 ring();16 ring(3);17 return 0;18}
这一课学会了
- 重载:几个函数同名,只要参数的个数或类型不同;只有返回类型不同不算。
- 编译器按实参挑:先找类型完全一样的,再找能「升一级」的(
char→int),再看别的转换。 - 两个一样合适就报错
call of overloaded ... is ambiguous;把实参写清楚,或者别写太像的重载。 - 默认参数
double discount = 0:调用时不给就用它;实参从左往右对上参数。 - 有默认值的参数要放在最后;默认值写在声明里,不在定义里再写。
- 能用默认参数解决的,就别再加一个重载,免得撞车。