函数基础:从调用到返回,C++函数的完整面貌
函数这东西,说白了就是一段可以反复调用的代码块。我刚开始学C++时觉得函数没啥了不起,不就是把代码包起来嘛。后来在项目中写了几万行代码才明白——函数设计的好坏,直接决定了代码能不能维护、能不能复用。
今天咱们就把函数这块彻底讲透。从定义声明,到参数传递的三种方式,再到重载、默认参数、内联函数,一个不落。
1. 函数定义与声明:先打招呼,再干活
函数声明就是告诉编译器:「嘿,有个函数长这样,参数是这些,返回值是那个。」而函数定义才是真正的实现代码。
我个人习惯把声明放在头文件里,定义放在源文件里。这样别的文件#include头文件就能调用,不用看到实现细节。
// 声明(通常在 .h 文件)
int add(int a, int b);
// 定义(通常在 .cpp 文件)
int add(int a, int b) {
return a + b;
}
我曾经在一个项目里把一个大函数的定义写在文件末尾,结果前面调用了二十多次,编译死活过不去。后来老老实实加了个声明在前面,一秒解决。
2. 参数传递:三种方式,各有千秋
参数传递是函数的核心机制。C++给了我们三种选择:值传递、指针传递、引用传递。你想想看,这三种方式本质区别在哪?
| 传递方式 | 是否拷贝 | 能否修改原值 | 适用场景 |
|---|---|---|---|
| 值传递 | 是(拷贝一份) | 不能 | 小对象、不需要修改原值 |
| 指针传递 | 否(传地址) | 能(通过指针修改) | 需要修改原值、可选参数(可传nullptr) |
| 引用传递 | 否(传别名) | 能 | 需要修改原值、避免拷贝大对象 |
值传递
void swap(int x, int y) {
int temp = x;
x = y;
y = temp;
// 外面不受影响,因为x和y是副本
}
指针传递
void swap(int* x, int* y) {
int temp = *x;
*x = *y;
*y = temp;
// 外面受影响
}
引用传递
void swap(int& x, int& y) {
int temp = x;
x = y;
y = temp;
// 外面受影响,写法更自然
}
我在项目中遇到过一个大坑——有个函数参数是个巨大的结构体,用了值传递。每次调用都要拷贝几十KB的数据,性能直接崩了。改成const引用后,速度提升了十几倍。嗯,这里要注意:大对象一定要用const引用传递,除非你确实需要一份副本。
3. 返回值:不只是return一个数
返回值是函数对外输出结果的方式。最基础的是return一个值,但C++还支持返回引用、返回指针。
// 返回值
int getValue() { return 42; }
// 返回引用(注意:不能返回局部变量的引用!)
int& getRef(int& x) { return x; }
// 返回指针
int* getPtr(int* p) { return p; }
4. 函数重载:同名不同参
函数重载允许你定义多个同名函数,只要参数列表不同就行。编译器会根据调用时传入的参数类型,自动匹配最合适的版本。
void print(int x) {
cout << "整数: " << x << endl;
}
void print(double x) {
cout << "浮点数: " << x << endl;
}
void print(const string& s) {
cout << "字符串: " << s << endl;
}
// 调用
print(42); // 匹配第一个
print(3.14); // 匹配第二个
print("hello"); // 匹配第三个
重载的规则很简单:参数个数不同,或者参数类型不同。返回值类型不能作为重载的依据——光靠返回值不同,编译器不知道你调用的是哪个版本。
5. 默认参数:少写点代码
默认参数让你在声明函数时给参数一个默认值。调用时如果没传这个参数,就使用默认值。
void showInfo(const string& name, int age = 18, const string& city = "北京") {
cout << name << " " << age << " " << city << endl;
}
// 调用
showInfo("张三"); // 张三 18 北京
showInfo("李四", 25); // 李四 25 北京
showInfo("王五", 30, "上海"); // 王五 30 上海
void f(int a = 1, int b),因为编译器不知道你调用 f(2) 时,这个2是给a还是给b。
我个人习惯把最常用的值设为默认参数。比如日志函数,默认级别是INFO,调用时如果不需要改级别,直接传消息就行,省事。
6. 内联函数(inline):用空间换时间
内联函数是C++提供的一种优化手段。在函数声明前加上inline关键字,编译器会尝试把函数调用替换成函数体代码,避免函数调用的开销(压栈、跳转、返回等)。
inline int max(int a, int b) {
return a > b ? a : b;
}
// 调用 max(3, 5) 时,编译器可能直接替换成 (3 > 5 ? 3 : 5)
但要注意,inline只是对编译器的建议,不是强制命令。如果函数体太大(比如超过几十行),编译器会忽略inline,按普通函数处理。
我曾经在一个性能敏感的系统里,把几个频繁调用的工具函数改成内联,整体性能提升了大约8%。虽然不多,但零成本啊——改个关键字就行。
- 声明在前,定义在后,这是规矩
- 参数传递:小对象值传递,大对象const引用传递,需要修改用引用
- 返回值:别返回局部变量的引用或指针
- 重载:参数不同就行,返回值不行
- 默认参数:从右往左,连续排列
- 内联:小函数用inline,大函数别用
函数是C++程序的基本积木。把这些基础打牢了,后面学类、模板、Lambda表达式都会轻松很多。说白了,函数设计得好,代码写起来就像搭积木一样顺畅。
公众号:蓝海资料掘金营,微信deep3321