必须用引用传递&:大对象只读时用const T&避免拷贝,需修改原对象时用T&;右值引用T&&用于移动语义;禁止将非const引用绑定临时对象;返回局部对象勿用std::move以免禁用NRVO;跨线程不可传栈变量引用。

什么时候必须用引用传递 &?
值传递会拷贝整个对象,对大对象(比如 std::vector、自定义类)意味着额外内存分配和构造/析构开销。如果函数只读不改,又不想拷贝,就得用 const T&;如果要修改原对象,就只能用 T&。
常见错误现象:传入一个大 std::string,函数里反复调用 .size() 或遍历,但性能差得离谱——其实是每次都在操作副本,且拷贝本身已拖慢执行。
使用场景:
- 函数需要修改调用方的变量(如
void swap(int& a, int& b)) - 参数是大型对象,且函数内部只读(如
bool validate(const Config& cfg)) - 重载运算符时,避免无谓拷贝(如
ostream& operator<<(ostream& os, const Person& p))
注意:不能把临时对象绑定到非 const 引用,比如 int& r = 5 + 3; 是错的;但 const int& r = 5 + 3; 合法,编译器会延长临时对象生命周期。
立即学习“C++免费学习笔记(深入)”;
std::move 不是“强制移动”,它只是转换类型
std::move 实际上只是把左值转成右值引用类型(T&&),不触发任何移动操作,也不保证移动发生。是否真移动,取决于目标函数是否重载了右值引用版本,以及该版本是否真的做了移动(而非拷贝)。
容易踩的坑:
- 对同一个对象多次
std::move:移动后对象处于“有效但未定义状态”,再次访问(如取值、调用非noexcept成员函数)可能崩溃 - 在函数返回时盲目加
std::move:比如return std::move(local_vec);,反而阻止了返回值优化(RVO),降低性能 - 把
std::move当“提速开关”:它解决的是资源转移问题(如指针所有权),不是所有场景都适用
参数差异:
-
T x→ 值传递,一定拷贝 -
const T& x→ 只读引用,零拷贝 -
T&& x→ 右值引用,通常用于承接临时对象或移动语义入口
函数返回局部对象时,别手动 std::move
现代 C++ 编译器对具名局部对象(Named Return Value Optimization, NRVO)和匿名临时对象都有强优化支持。直接 return obj; 就行,编译器大概率省掉拷贝甚至移动。
性能影响:加 std::move 反而可能禁用 NRVO,强制走移动构造(如果存在),而移动构造未必比优化后的直接构造快。实测中,对 std::vector<int></int> 这类类型,return v; 比 return std::move(v); 更快且更安全。
使用场景仅限:
- 返回参数(非局部对象),如
T&& get<em>ref() { return std::move(data</em>); } - 明确需要转移资源,且接收方是右值引用形参
兼容性注意:C++11 起支持 std::move,但 NRVO 在更早标准中已有,且各编译器实现稳定。不必为老标准妥协而回避返回值优化。
引用传递不能跨线程安全共享
引用本质是别名,不管理生命周期。把一个局部变量的引用传给新线程,等原函数返回,栈被回收,那个引用就指向垃圾内存——这是典型的 undefined behavior,不是偶尔出错,而是随时崩。
常见错误现象:
- 线程函数接收
const std::string& s,主线程传入局部std::string tmp = "hello";,然后立刻join()都救不了,因为tmp在线程启动前就销毁了 - lambda 捕获
[&x]后传给std::thread,x 是栈上变量
正确做法:
- 传值(
std::string s):安全,有拷贝成本但可控 - 传智能指针(
std::shared_ptr<t></t>):明确延长生命周期 - 用
std::move转移独占所有权(std::unique_ptr<t></t>)
别指望“我 join 了就没事”——线程启动时机不确定,栈帧销毁时机确定,中间的窗口期就是雷区。
传引用这事,看着省事,其实最考验对对象生命周期的理解。一不留神,不是慢一点,而是直接越界读写。









