在 C++ 中,默认参数也叫 “带默认值的参数”,指的是在定义函数时提前给形参指定一个默认初始值。
有了默认参数之后,我们在调用函数时就可以按需省略这个实参:如果省略不传,函数会自动使用默认值;如果主动传入了新实参,新值就会覆盖原来的默认值。
C++ 默认参数的基本用法
在 C++ 中,给函数参数设置默认值非常简单,只需要在参数列表中直接使用等号(=)给形参赋一个初始值即可。
语法:
返回类型 函数名(类型1 参数1, 类型2 参数2 = 默认值) {
// 函数体
}说明:
比如我们编写一个计算订单实付金额的函数 calcOrder(),其中商品金额每次都不同,但大多数订单的运费固定是 8 元,只有遇到包邮活动时运费才变为 0 元。这时我们就可以把运费参数 freight 的默认值设为 8。
示例 1:设置并使用默认参数
#include <iostream>
int calcOrder(int amount, int freight = 8) {
return amount + freight;
}
int main() {
// 省略第 2 个实参
std::cout << "普通订单实付:" << calcOrder(50) << '\n';
// 传入第 2 个实参
std::cout << "包邮订单实付:" << calcOrder(50, 0) << '\n';
return 0;
}运行结果如下。
普通订单实付:58
包邮订单实付:50分析:
int calcOrder(int amount, int freight = 8) 表示将第 2 个形参 freight 的默认值设为 8。当我们第 1 次调用 calcOrder(50) 时,省略了第 2 个实参,因此 freight 自动使用默认值 8,算出结果 58。
当我们第 2 次调用 calcOrder(50, 0) 时,明确传入了第 2 个实参 0,此时新传入的 0 会覆盖原来的默认值 8,算出结果 50。可以看到,默认参数并不是把形参删掉了,只是让我们在调用时可以按需省略这个实参。
C++ 默认参数的使用规则
在使用默认参数时,并不是想给哪个位置加默认值就能随便加,小伙伴们需要遵循以下 2 条语法规则。
1. 从右往左连续设置默认值
在一个函数的参数列表中,一旦某个参数设置了默认值,那么排在它右边的所有参数也都必须设置默认值。也就是说,默认参数必须从参数列表的 “最右侧” 开始连续往左设置,中间不能留出没有默认值的普通参数:
// 正确方式
int getVolume(int length, int width, int height = 1);
// 错误方式
int getVolume(int length, int width = 2, int height);在上面的代码中,正确方式把默认值放在了最右边的 height 上。而错误方式给中间的 width 设置了默认值,最右边的 height 却没有默认值,编译时就会直接报错。
为什么要这样规定呢?原因其实很简单:调用函数时,实参是按照从左往右的顺序依次赋给形参的。如果把带有默认值的参数放在左边、把普通参数放在右边,当我们少传一个实参时,编译器还是会先把实参赋给左边的参数,导致右边的普通参数拿不到值而报错。
同样地,在调用带有多个默认参数的函数时,省略实参也只能 “从右往左依次省略”,不能跳过中间的参数只给后面的参数传值。下面我们来看一个例子。
示例 2:连续省略右侧默认参数
#include <iostream>
// 计算长方体体积
int getVolume(int length, int width = 2, int height = 1) {
return length * width * height;
}
int main() {
std::cout << getVolume(5) << '\n';
std::cout << getVolume(5, 3) << '\n';
std::cout << getVolume(5, 3, 4) << '\n';
return 0;
}运行结果如下。
10
15
60分析:
在这个例子中,getVolume() 的第 1 个参数 length 没有设置默认值,每次调用时都必须传入。而后面的 width 和 height 都设有默认值,我们可以从最右侧开始逐个省略:
- getVolume(5):只传入第 1 个实参 5,后面的
width和height分别使用默认值 2 和 1,算出结果 10。 - getVolume(5, 3):传入前 2 个实参 5 和 3,只省略最右边的
height,height自动使用默认值 1,算出结果 15。 - getVolume(5, 3, 4):3 个实参全部传齐,不使用任何默认值,算出结果 60。
需要注意的是,调用时不能写成 getVolume(5, , 4) 这样空出中间的参数,否则会直接编译报错。如果我们想把第 3 个参数 height 改为 4,前面的第 2 个参数 width 也必须明确写出来,写成 getVolume(5, 2, 4) 才行。
2. 声明与定义分离时的写法
如果采用函数声明与定义分离的写法,大家要注意不能把默认参数写两遍,也就是在函数声明里写了一次默认值,到了下面的函数定义里又重复写一次。
在 C++ 中,同一个参数的默认值在同一个作用域内只能指定一次。当函数声明与函数定义分开写时,我们统一把默认值写在 “函数声明” 中,而 “函数定义” 处只写普通参数即可。
示例 3:在函数声明中指定默认值
#include <iostream>
#include <string>
// 1. 函数声明
void greet(std::string site = "绿叶网");
int main() {
greet();
greet("C++ 教程");
return 0;
}
// 2. 函数定义
void greet(std::string site) {
std::cout << "欢迎来到:" << site << '\n';
}运行结果如下。
欢迎来到:绿叶网
欢迎来到:C++ 教程分析:
在这个例子中,我们在 main() 上方的函数声明里将参数 site 的默认值设为 "绿叶网",而在下方的函数定义处只保留了 std::string site。由于唯一的参数已经设有默认值,第 1 次调用时直接写空括号 greet() 也是完全合法的。
另外,传递 std::string 这类只读对象时,我们也可以结合上一节学过的 const 引用传递来写。比如把声明写成 void greet(const std::string& site = "绿叶网");,既支持默认参数,又能省去复制开销。
为什么要把默认值写在函数声明里呢?因为编译器从上往下检查 main() 中的 greet() 时,必须先从前面的声明中看到默认值,才知道这里可以省略实参。如果在下面的函数定义处又重复写一遍 = "绿叶网",就会触发重定义默认参数的编译报错:
void greet(std::string site = "绿叶网");
void greet(std::string site = "绿叶网") {
std::cout << "欢迎来到:" << site << '\n';
}提示 如果函数没有单独拆分出函数声明,而是直接把完整的函数定义写在了 main() 前面,那么我们直接在函数定义的参数列表中指定默认值就可以了。
