在 C++ 开发中,一个经常让人困惑的问题是:
函数参数到底该写
T、T&,还是const T&?
很多人只记住一句话:
"大对象用 const 引用,小对象用值传递",
但背后的逻辑如果不理解,实际写代码时还是会犹豫。
这篇文章我们从 性能、语义、工程实践 三个角度,把参数传递方式一次讲清楚。
一、C++ 三种常见参数形式
1. 值传递(Pass by Value)
cpp
void f(T x);
特点:
- 会 拷贝一份对象
- 函数内部改
x,不会影响外部 - 简单直接
适用场景:
- 小类型(int / double / bool / enum)
- 函数需要"收下这份数据"
- 要存入成员变量 / 容器
2. 引用传递(Pass by Reference)
cpp
void f(T& x);
特点:
- 不拷贝,直接操作原对象
- 可以修改调用者的数据
适用场景:
- 明确要 修改调用者对象
- 工具函数 / 算法函数 / swap / normalize 等
3. 常量引用(Pass by Const Reference)
cpp
void f(const T& x);
特点:
- 不拷贝
- 不允许修改
- 性能好 + 语义清晰
适用场景:
- 大对象只读访问
- 打印 / 比较 / 计算 / 日志 / 序列化
- std::string / vector / map / 自定义类
二、为什么不用 const T(值传 const)?
很多人会写:
cpp
void f(const std::string s);
这其实 意义不大,因为:
- 仍然会拷贝
- const 只限制函数内部
- 外部对象本来就不会被影响
所以:
const T不能省拷贝,
const T&才能省拷贝。
三、性能视角:什么时候会浪费资源?
大对象
cpp
void f(std::string s); // 会拷贝
void f(const std::string& s); // 不拷贝
拷贝 string / vector 可能涉及:
- 内存分配
- 数据复制
- 构造析构
这是真正的资源浪费。
小对象
cpp
void f(int x); // 推荐
void f(const int& x); // 没必要
原因:
- int 只有 4 字节
- 引用反而传 8 字节地址
- CPU 寄存器传值更快
四、现代 C++ 的一个重要写法:值传 + move
当函数需要 保存参数 时,推荐这样写:
cpp
class User {
std::string name;
public:
void setName(std::string n) {
name = std::move(n);
}
};
优点:
- 左值调用:会拷贝
- 右值调用:直接移动
- 接口统一,调用舒服
这是现代 C++ 很常见的工程写法。
五、工程实践决策表
三个判断问题
1. 是否需要修改调用者?
-
是 →
T& -
否 → 继续
2. 是否是小类型?
-
是 →
T -
否 → 继续
3. 是否要保存参数?
-
是 →
T + move -
否 →
const T&
六、快速记忆口诀
可以直接背这一句:
改它:T&
小的:T
大的只读:const T&
要收下:T + move
七、典型示例对照
小类型
cpp
void setAge(int age);
大对象只读
cpp
void print(const std::string& s);
修改对象
cpp
void normalize(std::vector<int>& v);
存储对象
cpp
void setName(std::string name);
八、总结
C++ 参数传递的本质不是语法问题,而是:
- 性能
- 语义表达
- 工程可维护性
真正的原则不是死记硬背,而是理解:
我是"借来看一眼",
还是"要动它",
还是"要收下它"。
理解这一点,你写出的函数签名就会自然、专业、工程化。