C++右值引用与转移语义简要介绍
在 C++11 之前,值类型变量的传递会导致把它完整的拷贝一份
比如说把一个 vector 作为函数返回值赋值给某个局部变量,他就会调用 vector 的拷贝构造函数创建一个完整的副本,把这个副本作为函数返回的临时变量,然后把这个临时变量赋值给那个局部变量时又会再次拷贝构造
(不过这其实会被大多数编译器优化掉)
右值
上面例子中的临时变量就是一个右值
右值是无法获取到地址的值
大概可以理解为临时的变量或者没有被声明但是实际存在的变量
函数的返回值,还有常量都是右值
右值引用
根据名字我们知道它是一个右值的引用(废话)
使用右值引用接收右值可以让右值不被销毁,同时也不会拷贝任何数据
举个例子,下面代码中 str 变量接收了 GetStr 函数返回的右值
string GetStr() {
return "Hello world";
}
int main() {
string&& str = GetStr();
return 0;
}
转移构造函数
因为右值被使用过后就会被销毁,所以完整拷贝它的备份是很不划算的
我们希望把右值里的数据直接偷出来
那么我们可以通过转移构造函数来实现
class Array {
private:
int* arr;
int len;
public:
Array(int len) : arr(new int[len]) {}
~Array() {
if (arr) delete[] arr;
}
Array(const Array& other) : len(other.len) {
// 拷贝构造会导致整个数组被复制
arr = new int[other.len];
memcpy(arr, other.arr, len * sizeof(int));
}
Array(Array&& other) : arr(other.arr) {
// 转移构造可以直接把数组的指针偷过来
other.arr = NULL;
}
};
可以看到上面的代码把 other 这个右值里的 arr 偷走了
另外类似的还有转移赋值函数,也是使用右值引用传参,然后在函数里把右值里的东西偷出来
转移语义
那么又有一个问题,如果有一个左值,他以后不会再被用到,我希望能把它里面的东西偷出来放到另一个左值里,怎么实现
使用 std::move,把一个左值转换为右值,然后把它里面存的东西搬空
看这个例子就很清晰了
class Array {
private:
int* arr;
int len;
public:
Array(int len) : arr(new int[len]) {}
~Array() {
if (arr) delete[] arr;
}
Array(const Array& other) : len(other.len) {
// 拷贝构造会导致整个数组被复制
arr = new int[other.len];
memcpy(arr, other.arr, len * sizeof(int));
}
Array(Array&& other) : arr(other.arr) {
// 转移构造可以直接把数组的指针偷过来
other.arr = NULL;
}
};
int main() {
Array a(100);
Array b(move(a));
return 0;
}
可以看到变量 a 里存的数组指针被变量 b 偷出来了,从而避免了整个数组的拷贝