一、左值(Lvalue)与右值(Rvalue)
1. 基本定义
- 左值:有持久状态的对象,可以取地址,能出现在赋值号左侧。
int a = 10;
int* p = &a;
- 右值:临时对象,没有持久状态,不能取地址,通常出现在赋值号右侧。
- 纯右值(prvalue):字面量、表达式结果。
42;
func();
a + b;
- 将亡值(xvalue):通过
std::move
转换或返回右值引用的表达式。std::move(a);
2. 关键特性
- 左值可绑定到左值引用(
T&
),右值可绑定到右值引用(T&&
)。 - 右值引用的核心用途:实现移动语义和完美转发。
二、拷贝控制:深拷贝与浅拷贝
1. 浅拷贝(Shallow Copy)
2. 深拷贝(Deep Copy)
三、拷贝构造函数与移动构造函数
1. 拷贝构造函数(Copy Constructor)
2. 移动构造函数(Move Constructor)
3. 何时触发?
- 拷贝构造:对象初始化、传参(非移动语义)、返回对象(未优化时)。
- 移动构造:对象初始化使用临时对象、显式
std::move
、返回局部对象(优化后)。
四、std::move
与移动语义
1. std::move
的作用
2. 使用场景
五、std::forward
与完美转发
1. 完美转发(Perfect Forwarding)
- 目标:在泛型代码中保留参数的原始值类别(左值/右值)。
- 结合通用引用(
T&&
)和引用折叠规则:
T& &
→ T&
T&& &
→ T&
T& &&
→ T&
T&& &&
→ T&&
2. std::forward
的用法
3. 示例对比
- 无
std::forward
:wrapper(42);
- 有
std::forward
:wrapper(42);
六、关键总结
概念 | 核心用途 |
---|
深拷贝 | 避免资源重复释放,独立管理资源 |
移动语义 | 提升性能,转移资源所有权 |
std::move | 强制使用移动操作(左值→右值) |
std::forward | 泛型编程中保持参数值类别(完美转发) |
七、完整代码示例
#include <utility>
class Resource {
public:
int* data;
Resource(int val) : data(new int(val)) {}
Resource(const Resource& other) : data(new int(*other.data)) {}
Resource(Resource&& other) noexcept : data(other.data) {
other.data = nullptr;
}
~Resource() { delete data; }
};
template<typename T>
void process(T&& arg) {
Resource res(std::forward<T>(arg));
}
int main() {
Resource a(10);
Resource b = a;
Resource c = std::move(a);
process(Resource(20));
process(b);
}
八、注意事项
- 移动后的对象状态:被移动的对象应处于有效但未定义的状态(如
nullptr
),避免继续使用。 - 异常安全:移动构造函数应标记为
noexcept
,否则某些标准库操作(如vector
扩容)可能回退到拷贝。 - 规则三/五原则:自定义拷贝构造/赋值时,通常需要同时定义移动操作,反之亦然。