C++中所有的值都可以分为左值和右值。
左值
左值是“有身份“的值。一个左值一定是能够被取地址的。
int x = 5; // x 是左值
int& ref = x; // ref 是左值引用,绑定到左值 x
左值通常是程序中可持久化存在,能被频繁访问的对象。左值一定会被分配一个可操作的地址。
左值的生命周期通常较长,一般超出单个表达式的作用域。
右值
右值有两个核心特征: 可移动性 和 临时性。
C++11 之后,右值也可以分为两类。
- 纯右值
- 将亡值
纯右值
纯右值是纯粹的临时值,没有身份,不可以取地址。
通常,纯右值用来表示计算结果,非引用返回值,构造函数调用生成的临时对象。
thread(func); // 这样的一个值是纯右值。
一个纯右值如果没有一个左值来接收,那么这个纯右值将会立即死去。
将亡值
将亡值是一个具有身份但即将被销毁的值。同样地,一个将亡值如果没有一个左值来接收,这个将亡值也会立即死去。
将亡值通常是被move等标记过的值,或者右值引用返回值。
通过将亡值,C++支持了移动语义。
利用移动语义,我们可以将资源从一个实例转移到另一个实例。在某些情况下,这十分关键。
例如,智能指针unique_ptr具有独占所有权的机制。所以在unique_ptr间转移资源时,移动语义非常有用。
// 将一个unique_ptr的资源转移到另一个unique_ptr
unique_ptr<string>ptr1 = make_unique<string>(str);
unique_ptr<string>ptr2 = move(ptr1);
其中move()的作用是让左值ptr1变成一个将亡值。
左值引用、右值引用与移动语义
C++11后,编译器支持使用&&来表示一个右值引用。
int a = 10; // 左值
int & ref = a; // 左值引用
int && ref2 = a; // 右值引用
move可以将一个左值转化为一个右值引用,以便触发移动构造函数,从而使用移动语义。
与拷贝构造函数类似,移动构造函数也是通过不同的参数重载的构造函数。
String(String&& other)
: data_(other.data_), size_(other.size_) { // 1. 窃取资源
// 2. 置空源对象,避免重复释放
other.data_ = nullptr;
other.size_ = 0;
}
在移动构造函数中,应该将管理资源的原裸指针置空。对于STL中的容器或者std::unique_ptr,应该使用move进行资源的移动。
而对于其他内置数据类型,则不必过多关心。
评论