默认参数
更新时间:2026-08-27。本文是
languages/cpp/主题入门层第 12 篇。调用函数时少传几个参数也能编译通过,靠的是默认参数。它和重载是孪生兄弟,经常一起出场;但"默认值只写在声明里"这条规则和重载叠加时的歧义,是两处必踩的坑。
本文要回答的问题
- 默认参数怎么写?调用时怎么省?
- 为什么默认值只能从右往左给?
- 默认参数和重载放一起会出什么乱子?
一、基本写法:调用方少传,函数兜底
void greet(const std::string& name, int repeat = 1) {
for (int i = 0; i < repeat; ++i) {
std::cout << "Hello, " << name << "!\n";
}
}
greet("Alice"); // repeat 用默认值 1
greet("Bob", 3); // 显式传 3第二个参数 repeat 带 = 1,调用时可不传,编译器自动补上。效果上有点像"两个重载合一":
void greet(const std::string& name) { greet(name, 1); }
void greet(const std::string& name, int repeat) { /* ... */ }默认参数本质上就是编译器帮你生成的重载,理解这一点很多疑惑就解开了——所以上一篇说它俩是孪生兄弟。
二、规则一:默认值必须从右往左连续给
void f(int a, int b = 2, int c = 3); // 合法:从右边开始
void g(int a = 1, int b, int c = 3); // 非法:中间缺了原因是调用时参数是按位置匹配的:f(5) 表示 a=5,b、c 用默认;如果允许 g(5),编译器没法判断 5 给谁——所以 C++ 规定:一旦某个参数有默认值,它右边的所有参数都得有默认值。
三、规则二:默认值只写在声明(头文件)里
这是工程上最阴的坑。声明和定义分开时:
// foo.h
void draw(int width, int color = 0xFF0000);
// foo.cpp
void draw(int width, int color) { // 这里不要写 = 0xFF0000!
// ...
}默认值写在声明处,定义处不能重复写——重复写且值不同直接编译错误。为什么要这样?因为调用方编译时只看头文件,默认值必须让所有调用者都看见。所以:默认参数属于接口,放头文件的声明里。
如果只在定义里写默认值而头文件声明没写,调用方(只看到声明)编译时会报"参数太少",一头雾水。这条规则记住了,能省不少排查时间。
四、坑:默认参数 × 重载 = 歧义
默认参数本质是生成重载,再叠真重载就可能撞车:
void print(int n) {}
void print(int a, int b = 5) {} // 相当于 print(int) + print(int, int)
print(3); // 错误:两个 print(int) 撞上了,调用不明确写的时候保持清醒:有了默认参数就别再加同参重载。反过来,重载和默认参数怎么选?经验法则:参数有"绝大多数情况用同一个值"的倾向,用默认参数;不同调用意图差异大,用重载。
五、和 C 对比
| 对比项 | C | C++ |
|---|---|---|
| 默认参数 | 没有 | void f(int a, int b = 2); |
| 模拟方式 | 手写两个函数 / 宏 | 原生支持 |
| 默认值放哪 | — | 声明(头文件) |
| 从右往左规则 | — | 必须连续给到最右 |
| 与重载叠加 | 无重载 | 可能歧义,谨慎 |
C 想模拟默认参数很别扭:要么写两个函数,要么用宏或 NULL 判断。C++ 直接内建,这也是"带类的 C"之外的日常便利。
六、与本站主线衔接
- 默认参数在构造函数的成员初始化里大量出现,下一篇开始进入引用,然后是类与对象(16 篇起);
- 重载决议与隐式转换的完整规则,见高手层模板与泛型编程;
- C 的对照与函数声明细节,见 C 函数声明、头文件与原型。
一句话总结
默认参数让"可省"的参数在调用时隐身,编译器自动补上,本质是帮你生成重载;默认值必须从右往左连续给、且只写在声明(头文件)里;和真重载叠加容易撞出"调用不明确",两个版本能匹配同一实参时就是设计失误。