目录
本篇写的主要是关于函数基础提高的部分,函数提高主要体现在函数的灵活设计方面,由此来实现代码的简介高效。本篇主要解析默认参数,占位参数以及函数重载三个基础但是重要的特性。
1.默认参数
默认参数是函数定义时为参数指定的预定义值。当函数调用的时候未提供该参数的值,函数将自动使用默认值。
1.基础语法
C++中默认参数可以直接在函数声明或者定义中指出。基础的语法为
返回类型 函数名(参数类型1 参数名1 = 默认值1, 参数类型2 参数名2 = 默认值2, ...)
{
// 函数体
}
(也就像是定义函数的时候直接进行初始化),但是需要注意的是实际上函数运行时参数的值还是以实际赋值为准,例如
#include<iostream>
using namespace std;
int add(int a,int b = 20,int c = 30)
{
return a + b + c;
}
int main()
{
cout << add(10, 30) << endl;
//得出的结果是70 说明函数的实际参数还是以实际赋值为准
return 0;
}
2.注意事项
1.默认参数必须从右向左连续设置
具体阐述的话就是说如果参数的某一项已经进行了初始化,那么它后面的每一项都应该进行初始化
不然像上面这段代码实际上只赋值了两个,如果第三个参数不进行默认初始化的话不能分辨出我实际初始化的是第二个变量还是第三个变量。也就是说给未初始化的参数和按顺序先出现的参数的实际赋值优先顺序出现了矛盾。
2.函数声明和定义初始化的重复性
函数声明中如果进行了参数的默认 那么后面对于函数的定义就不能够再进行参数的默认初始化。
// 声明(指定默认值)
int add(int a = 0, int b = 0);
// 定义(不重复默认值)
int add(int a, int b) {
return a + b;
}
再例如类成员函数中的默认参数
class Rectangle {
public:
// 成员函数设置默认参数
int calculateArea(int length = 5, int width = 10);
};
// 定义时无需重复默认值
int Rectangle::calculateArea(int length, int width) {
return length * width;
}
其实只在定义的时候进行默认参数初始化也是可以的,但是这就违背了.h中声明和.cpp中定义分离的原则。(保持接口的可见性)
综上默认参数使用场景为:高频函数简化调用
2.占位参数
1.基础语法实现
int add(int a,int)// 参数占位的地方主要是只要有这个参数的数据类型就行 所以也正是应了传参参数名称随意的特性
{
;
}
int main()
{
add(10, 10);
return 0;
}
占位参数核心特点:在函数定义中存在,但在函数体内不直接使用。
2.主要作用
1.接口兼容性
当需要通过参数增加扩展函数功能时,新增参数可能破坏现有的代码。占位参数允许保留原始的接口,同时为未来扩展保留位置。
// 新版本函数(添加y参数,但保留旧接口)
void newFunc(int x, int y = 0) { // y是占位参数,默认值0
if (y != 0) {
// 新逻辑:使用y
printf("新逻辑:x = %d, y = %d\n", x, y);
} else {
// 兼容旧逻辑
printf("旧逻辑:x = %d\n", x);
}
}
就是说为未来需求做好准备,而目前y默认参数不会影响代码逻辑的运行。
2.语法强制要求
某些语言或框架要求函数遵循特定的参数格式,即使部分参数不需要使用。
示例:
- C++ 的后置递增操作符
operator++(int)
必须带一个占位参数以区分前置递增。 - 事件回调函数通常需要符合固定的参数签名(如
void callback(int status, void* data)
)。
3.函数重载
在同一作用域内定义多个名称相同但是参数不同的函数,编译器会根据调用时的参数类型,数量或者顺序自动选择合适的函数实现。
1.函数重载的基本规则
1.处于同一作用域下面(全局作用域,局部作用域,类作用域,命名空间作用域)
2.参数列表必须不同
3.函数的名称相同
void func()
{
cout << "func的调用" << endl;
}
void func(int a)
{
cout << "func(int a)的调用" << endl;
}
int main()
{
func;//对应上面第一个函数
int a = 10;
func(a);//对应上面第二个函数
}//这两个函数的作用域都在全局作用域内
2.函数重载的匹配原则
1.精准匹配:参数类型完全一致
2.隐式转换匹配:参数类型可以通过隐式转换匹配
(隐式转换补充:1.安全转换优先:编译器倾向于执行无损转换例如int->double)
2.表达式中的类型提升:不同类型参与运算的时候,较低精度的数据会自动提升为较高精度的数据
3.目标类型明确:转换方向由赋值语句左侧类型决定
)
3.函数重载的注意事项
1.参数是否有const修饰
参数是否加const也可以作为不同函数重载的满足条件
因为int&只能绑定非常量左值(比如变量),但是const int&可以绑定常量左值和右值
void func(int& x); // 版本1:非常量引用
void func(const int& x); // 版本2:常量引用
int main() {
int a = 10; // 非常量左值
const int b = 20; // 常量左值
func(a); // 调用版本1:a是非常量左值,优先匹配int&
func(b); // 调用版本2:b是常量左值,只能匹配const int&
func(30); // 调用版本2:30是右值,只能匹配const int&
}
当然会有朋友疑惑为什么不能是值传递而是引用传递呢
这是因为值传递是通过拷贝进行的,所以说形参本质上是独立的拷贝副本,无论实参是否是const常变量,拷贝之后形参都是变量,所以说两种类型重载函数都可以匹配上,这时候就违反了函数重载规则。
2.函数重载碰到默认参数
这个时候可能对于接收的是哪个参数发生混淆
void func(int x) {
cout << "func(int): " << x << endl;
}
void func(int x, int y = 10) {
cout << "func(int, int): " << x << ", " << y << endl;
}
int main() {
func(5); // 错误:调用歧义,不知道调用哪个函数
return 0;
}
4.后话
这篇是对于函数基础提高的小结,希望能够对大家学习提供一些帮助。
不足或者错误之处欢迎大家纠正指出。
期待和大家的交流!