C++: 函数重载与默认参数
最后更新:2026-08-26
有时候,你需要一个函数能处理不同类型或不同数量的参数。
比如
C++ 提供了两个特性来实现这个——函数重载和默认参数。
1. 函数重载(Function Overloading)
(1) 1.1 什么是函数重载?
函数重载允许你定义多个同名函数,但它们的参数列表不同。
| 函数签名 | 含义 |
|---|---|
print(int x) |
打印整数 |
print(double x) |
打印浮点数 |
print(const std::string& x) |
打印字符串 |
调用时,编译器会根据实参的类型自动选择正确的函数。
▶ 示例 1:函数重载(难度⭐)
#include <iostream>
#include <string>
// 重载函数:打印整数
void print(int x) {
std::cout << "整数:" << x << std::endl;
}
// 重载函数:打印浮点数
void print(double x) {
std::cout << "浮点数:" << x << std::endl;
}
// 重载函数:打印字符串
void print(const std::string& x) {
std::cout << "字符串:" << x << std::endl;
}
int main() {
print(5); // 调用 print(int)
print(3.14); // 调用 print(double)
print("Hello"); // 调用 print(const char*)
return 0;
}
输出:
整数:5
浮点数:3.14
字符串:Hello
⚠️ 重点: 函数重载的条件是参数列表不同(类型不同、数量不同、或顺序不同)。返回类型不同不算重载。
2. 函数重载的规则
(1) 2.1 什么算"参数列表不同"?
| 不同点 | 示例 |
|---|---|
| 类型不同 | void foo(int x) vs void foo(double x) |
| 数量不同 | void foo(int x) vs void foo(int x, int y) |
| 顺序不同 | void foo(int x, double y) vs void foo(double x, int y) |
(2) 2.2 什么不算重载?
| 情况 | 示例 | 结果 |
|---|---|---|
| 只有返回类型不同 | int foo() vs double foo() |
❌ 编译报错:重定义 |
| 只有参数名不同 | void foo(int x) vs void foo(int y) |
❌ 编译报错:重定义 |
3. 默认参数(Default Arguments)
(1) 3.1 基本用法
默认参数允许你在调用函数时省略某些实参,编译器会自动用默认值。
#include <iostream>
// 声明时指定默认参数
void print(int x, int y = 10) {
std::cout << "x = " << x << ", y = " << y << std::endl;
}
int main() {
print(5); // 只传一个参数,y 用默认值 10
print(5, 20); // 传两个参数,y 被改成 20
return 0;
}
(2) 3.2 默认参数的规则
- 只能从右往左设置默认值
void foo(int a, int b = 10, int c = 20); // ✅ 正确
void bar(int a = 5, int b, int c); // ❌ 错误:a 有默认值,但 b 没有
- 默认参数只能在函数原型或函数定义中指定一次
int add(int a, int b = 10); // 在函数原型中指定默认值
int main() {
std::cout << add(5) << std::endl;
return 0;
}
int add(int a, int b) { // ✅ 定义中不用再写默认值
return a + b;
}
4. 函数重载 + 默认参数
函数重载和默认参数可以一起用,让函数更灵活。
▶ 示例 2:打印带分隔符的数组(难度⭐⭐)
#include <iostream>
#include <string>
// 重载 + 默认参数
void printArray(int arr, int length) {
printArray(arr, length, ", "); // 调用下面那个函数,用默认分隔符
}
void printArray(int arr, int length, const std::string& delimiter) {
for (int i = 0; i < length; i++) {
std::cout << arr[i];
if (i < length - 1) {
std::cout << delimiter;
}
}
std::cout << std::endl;
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
printArray(arr, 5); // 用默认分隔符 ", "
printArray(arr, 5, " - "); // 用自定义分隔符 " - "
return 0;
}
输出:
1, 2, 3, 4, 5
1 - 2 - 3 - 4 - 5
5. 实战:简单的日志函数
▶ 示例 3:带日志级别的打印函数(难度⭐⭐)
#include <iostream>
#include <string>
#include <ctime>
// 日志级别
enum LogLevel {
INFO,
WARNING,
ERROR
};
// 获取当前时间字符串
std::string getCurrentTime() {
std::time_t now = std::time(nullptr);
std::tm* tm = std::localtime(&now);
char buffer[80];
std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm);
return std::string(buffer);
}
// 重载函数:打印日志
void log(const std::string& message) {
log(message, INFO); // 默认日志级别:INFO
}
void log(const std::string& message, LogLevel level) {
std::string levelStr;
if (level == INFO) {
levelStr = "INFO";
} else if (level == WARNING) {
levelStr = "WARNING";
} else {
levelStr = "ERROR";
}
std::cout << "[" << getCurrentTime() << "] [" << levelStr << "] " << message << std::endl;
}
int main() {
log("程序启动"); // 用默认日志级别
log("内存不足", WARNING);
log("文件打开失败", ERROR);
return 0;
}
输出:
[] []
输出(示例):
[2026-06-28 22:30:00] [INFO] 程序启动
[2026-06-28 22:30:01] [WARNING] 内存不足
[2026-06-28 22:30:02] [ERROR] 文件打开失败
6. 常见错误
(1) 6.1 函数重载歧义
错误示例:
#include <iostream>
void foo(int x) {
std::cout << "foo(int)" << std::endl;
}
void foo(double x) {
std::cout << "foo(double)" << std::endl;
}
int main() {
foo(5); // ✅ 调用 foo(int)
foo(3.14); // ✅ 调用 foo(double)
foo('A'); // ❌ 歧义:char 可以转换成 int 或 double
return 0;
}
编译错误信息:
error: call to 'foo' is ambiguous
修复: 显式类型转换:
foo(static_castint('A')); // 调用 foo(int)
foo(static_castdouble('A')); // 调用 foo(double)
(2) 6.2 默认参数导致重载歧义
错误示例:
#include <iostream>
void foo(int x) {
std::cout << "foo(int)" << std::endl;
}
void foo(int x, int y = 10) {
std::cout << "foo(int, int)" << std::endl;
}
int main() {
foo(5); // ❌ 歧义:可以调用 foo(int) 或 foo(int, int)(用默认参数)
return 0;
}
修复: 删除其中一个重载,或者改参数类型。
❓ 常见问题
Q:函数重载和 C 语言的函数有什么区别? A:C 语言不支持函数重载,因为 C 编译器的函数名修饰规则很简单(基本就是函数名本身)。
C++ 编译器会对函数名进行名称修饰(Name Mangling),把参数类型信息编码到函数名里,所以同名函数能共存。
Q:默认参数能是变量吗? A:可以,但必须是全局变量或静态变量(后面会学)。
CPPint defaultY = 10; void foo(int x, int y = defaultY) { // 可以是变量 // ... }
Q:函数重载能提高程序运行效率吗? A:不能。函数重载是编译时特性(编译器根据实参类型选择函数),运行时效率和普通函数调用一样。
它提高的是开发效率(代码更简洁、易读)。
📖 小节
- 函数重载允许定义多个同名函数,但参数列表必须不同
- 默认参数允许省略某些实参,编译器用默认值补齐
- 默认参数只能从右往左设置
- 函数重载 + 默认参数可以让函数更灵活
- 注意避免重载歧义
📝 作业
-
基础题 (Difficulty ⭐): 写一个重载函数
int max(int a, int b)和double max(double a, double b),分别返回两个整数和两个浮点数的最大值。 -
进阶题 (Difficulty ⭐⭐): 写一个函数
void drawRectangle(int width, int height, char fillChar = '*'),打印一个矩形。
输入:drawRectangle(5, 3, '#')
输出:
#####
#####
#####
(当不传 fillChar 时,用默认的 *)
- 挑战题 (Difficulty ⭐⭐⭐): 写一个程序,实现"多功能计算器":
- 用函数重载实现
add、subtract、multiply、divide函数,支持int和double两种类型 - 用默认参数设置"保留小数位数"(默认 2 位)
- 在
main函数里测试所有函数
- 函数重载:同名不同参数(数量或类型不同)
- 重载决策:编译器根据实参类型匹配最合适的版本
- 默认参数和重载混用时注意二义性
- 不能仅按返回类型重载
- 重载让代码更直观,用同一函数名处理不同类型
7. 🚀 下一步
学会了函数的高级用法,接下来我们学习 作用域与生命周期(第14课)—— 理解变量在哪里能被访问、能存在多久,这是理解程序行为的关键!