🔷 第六章 函数
📄 一句话概括

重载 = 同一个函数名、多套参数表。调用时编译器看实参的个数和类型,决定进哪一个版本。区分只靠参数表——返回值不同、参数名不同都不算重载。

📝 本节和前面几节的关系

6.4 讲的"同名遮蔽"是不同作用域里的同名;这一节反过来——在同一个作用域里故意同名。其实你早就用过重载了:cout << 能打印整数、小数、字符、字符串,因为 << 运算符本身被重载过(第二章就在享受这个便利)。用到的旧知识:函数头三部件(6.1.1)、类型转换楼梯(3.3.2)。

一、写法与效果

void print(int n)         { cout << "整数:" << n << endl; }
void print(double d)      { cout << "小数:" << d << endl; }
void print(char c, int n) { for (int i = 0; i < n; i++) cout << c; cout << endl; }
print(5);          // → 进 print(int)
print(3.14);       // → 进 print(double)
print('*', 6);     // → 进 print(char, int)

名字完全一样,做的事按参数自动分派——这就是"同名多面"。好处:调用方不用记一堆名字(不用 printInt/printDouble/printChars),语义一致的操作用一个名字就够。

二、靠什么区分(重载的判据)

能区分(合法重载) 不能区分(编译错误)
参数个数不同:f(int) / f(int,int) 只有返回类型不同:int f() / double f()
参数类型不同:f(int) / f(double) 只有参数名不同:f(int a) / f(int b)
参数顺序不同:f(int,double) / f(double,int) —

一句话:参数表就是唯一的身份证。

三、编译器怎么选(三步)

以 print(3.14) 为例:

  1. 先找完全匹配——有 print(double) 吗?有 → 就是它
  2. 没有就找能隐式转换的——比如没有 print(int) 时传 5,int → double 能转(3.3.2 的楼梯,只上不下),就用 print(double)
  3. 多个都能转 → 报错——这叫二义性(ambiguous),编译器拒绝替你猜

⚠️ 二义性的经典现场:

void f(int x);
void f(double x);
f('A');           // char 既能转 int、也能转 double → 报错 ambiguous

四、实例 06:定义重载函数

#include <iostream>
using namespace std;

void show(int n) {                      // ① 一个整数
    cout << "整数:" << n << endl;
}

void show(double d) {                   // ② 一个小数
    cout << "小数:" << d << endl;
}

void show(char c, int n) {              // ③ 一个字符 + 一个次数
    cout << "字符串:";
    for (int i = 0; i < n; i++) cout << c;
    cout << endl;
}

int main() {
    show(42);              // ①
    show(3.14);            // ②
    show('*', 5);          // ③
    show(7.0f);            // float → 隐式提升成 double,进 ②
    return 0;
}

看到什么算成功:

整数:42
小数:3.14
字符串:*****
小数:7

注意最后一行 show(7.0f):传的是 float,但没有 show(float) 这个版本——编译器把它隐式提升成 double(3.3.2 的"楼梯只上不下"),进了 ② 号门。这说明重载匹配允许隐式转换,同时也说明转换多了就有歧义风险。

30 秒实验:加一个 void show(float f) 版本,再调 show(7.0f)——现在它会精确匹配新版本;而 show(42) 反而要让编译器在 "int→float" 和 "int→double" 之间做选择,有些编译器会在这里报歧义。体会:重载版本越多,越要小心歧义。

五、常见误区

  1. "返回值不同也能重载"——不能,而且会报"重复定义"。只有参数表能区分。
  2. "只写声明就算重载"——每个重载版本都必须有完整的定义。
  3. "重载是把原来的覆盖了"——不是覆盖,是并存。编译器按实参挑一个用,其余的都还在。
  4. "重载和默认参数可以随便混"——容易出歧义:f(int a, int b = 0) 与 f(int a) 同时存在时,f(5) 两边都能接 → 报错。两者尽量择一使用。
  5. "重载是运行时判断的"——不是,它完全发生在编译期,运行时零额外开销。这一点和后面章节的"虚函数(多态)"完全不同。
自测三问(点开对答案) 1. 重载靠什么区分?——参数表:参数的个数、类型、顺序;返回值不同不算。 2. 什么是二义性?——多个重载版本都能通过隐式转换接受同一组实参,编译器无法选择。 3. 重载是编译期还是运行期决定?——编译期,运行时没有额外开销。

相关笔记