应优先用const t&接收大型左值对象以避免拷贝,用t&&接收临时对象并主动移动资源;rvo不可控,需从参数类型和资源管理入手;移动构造函数须noexcept且置空源指针。

直接用右值引用接收临时对象,或用 const 左值引用接收已存在对象,能避免复制。移动构造函数和 RVO 是底层保障,但不能依赖它们“自动解决”所有场景。
用 const T& 接收已存在的大型对象
这是最常用、最安全的方式,适用于函数参数中需要读取但不修改的大型对象(如 std::vector、自定义大结构体)。
- 不会触发拷贝构造,也不要求类型支持移动语义
- 编译器无法对
const T&做移动(因为禁止修改源),所以它天然规避了“想移动却误用了拷贝”的风险 - 注意:不能用于接收临时对象后还继续使用该临时对象——虽然语法允许,但生命周期只到完整表达式结束
示例:void process(const std::vector<int>& data)</int> —— 传入 vec 或 get_data() 都合法,但后者若返回临时对象,调用完就析构。
用 T&& 接收临时对象并主动移动
当你明确知道参数是临时值(比如函数返回值、字面量构造),且希望把资源所有权转移进来时,才用右值引用。
- 必须配合移动构造/赋值逻辑,否则只是“多了一层引用”,没实际收益
- 不能接受左值(如命名变量),除非显式调用
std::move,但这会破坏原对象状态,需谨慎 - 常见误用:把
void f(std::vector<int>&& v)</int>当成“通用高性能入口”,结果调用方传左值时编译失败,或滥用std::move导致后续访问空指针
示例:void append(std::vector<int>&& v) { data_.insert(data_.end(), v.begin(), v.end()); }</int> —— 这里没“移动”v本身,只是读取,其实更适合用 const std::vector<int>&</int>。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
RVO/NRVO 不是你能控制的优化手段
函数返回大型对象时,编译器可能省略拷贝(C++17 起 RVO 强制),但你不能靠它来设计接口。尤其注意:
- NRVO(命名返回值优化)在多 return 路径、条件分支下大概率失效
- 即使有 RVO,调用方声明变量时仍可能触发移动构造(如
auto x = f();在 C++17 前) - 如果你写了移动构造函数但没加
noexcept,某些容器(如std::vector::resize)会退回到拷贝,性能断崖下跌
所以,别把性能押注在“编译器会不会优化”上,而要从参数类型和资源管理策略入手。
构造函数内部怎么避免深拷贝
当你的类持有动态资源(如 char*、std::unique_ptr),构造函数接收大型对象时,关键不是“怎么传”,而是“怎么存”:
- 优先用
std::unique_ptr或std::vector管理资源,它们自带移动语义,无需手写移动构造函数 - 如果必须手写移动构造函数,记得加
noexcept,并把源对象的指针置为nullptr,防止双重释放 - 不要在构造函数里做深拷贝逻辑(如
memcpy大块内存),除非你确定调用方永远只传小对象
示例错误写法:DataBuffer(const DataBuffer& other) : size_(other.size_) { data_ = new char[size_]; memcpy(data_, other.data_, size_); } —— 这是典型拷贝构造,不是移动;正确做法是提供 DataBuffer(DataBuffer&&) noexcept 并转移指针。
真正容易被忽略的点是:移动语义只有在“资源可转移”时才有意义。如果类里全是栈成员(int、std::array),移动和拷贝性能几乎一样,强行加移动构造反而增加维护成本。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










