共计 1574 个字符,预计需要花费 4 分钟才能阅读完成。
1. 为什么我们需要关注函数参数传递?
作为一个 C ++ 新手,我在写第一个交换两个变量的函数时就踩了坑:
void swap(int a, int b) { // 这样写根本交换不了!int temp = a;
a = b;
b = temp;
}
后来才知道原来 C ++ 有 三种截然不同的参数传递方式,选错了轻则性能下降,重则程序崩溃。

1.1 新手常见误区
- 无脑用值传递:拷贝大对象(如 vector)时会产生性能黑洞
- 指针滥用:忘记检查空指针或搞错内存生命周期
- 忽视 const:该用 const 引用时没加,导致意外修改
2. 三种调用方式原理拆解
2.1 值传递(Pass by Value)
内存行为:
1. 调用时在栈上创建参数的完整副本
2. 原变量和副本完全独立
void process(std::string s) {/* 修改 s 不影响外部 */}
int main() {
std::string data = "Hello";
process(data); // 这里会发生 std::string 的拷贝构造
}
适用场景:
– 需要保护原始数据不被修改
– 传递内置类型(int/double 等)
– 函数内需要修改参数但不影响外部
2.2 指针传递(Pass by Pointer)
内存行为:
1. 传递变量地址的副本(指针本身被拷贝)
2. 通过解引用可修改原始数据
void resize(int* arr, int newSize) {if (arr != nullptr) // 必须检查!*arr = newSize; // 解引用修改
}
适用场景:
– 需要表示可选参数(用 nullptr)
– C 风格 API 兼容
– 明确需要指针语义(如链表节点)
2.3 引用传递(Pass by Reference)
内存行为:
1. 传递变量的别名(底层仍用指针实现)
2. 语法更简洁,天然非空
void capitalize(std::string& s) {if (!s.empty())
s[0] = toupper(s[0]);
}
最佳实践:
– 输出参数(函数需要修改外部变量)
– 传递大对象避免拷贝
– 必须配合 const 使用:void print(const auto& obj)
3. 性能对比实测
测试代码片段:
struct BigData {char data[1024]; };
// 测试值传递
void byValue(BigData b) {/*...*/}
// 测试 const 引用
void byRef(const BigData& b) {/*...*/}
使用 -O2 编译后的关键汇编对比:
– 值传递:call memcpy@PLT(明显拷贝开销)
– 引用传递:直接操作原内存地址
4. 避坑指南
-
悬空引用:
string& badFunc() { string local = "危险!"; return local; // 局部变量销毁后引用失效 } -
指针所有权混淆:
void process(int* p) {delete p; // 谁知道该不该在这里 delete?} -
意外修改:
void print(vector<int> data) { // 本意只是读取 data.clear(); // 但实际修改了拷贝}
5. 实战思考题
问题:设计一个字符串处理函数,要求:
– 必选参数用 const 引用
– 可选参数用指针
– 输出参数用非 const 引用
参考答案结构:
void stringTransform(
const std::string& input, // 必选输入
std::string& output, // 输出参数
int* optionalParam = nullptr // 可选参数
) {if (optionalParam)
output = input.substr(0, *optionalParam);
else
output = input;
}
6. 总结心得
经过这次深度探索,我发现:
– 小对象(<8 字节)直接传值反而更快
– 引用传递是日常最常用的方式
– 现代 C ++ 中原始指针应逐步被智能指针替代
建议搭配《Effective C++》第 20-22 条一起阅读,会有更体系化的理解。
正文完
