C++右值引用
Maxwell.. 人气:01、右值
1.1 简介
首先区分一下左右值:
- 左值是指存储在内存中、有明确存储地址(可取地址)的数据;
- 右值是指可以提供数据值的数据(不可取地址)
如int a=123;123是右值, a是左值。总的来说 可以对表达式取地址(&)就是左值,否则为右值
而C++11 中右值又可以分为两种:
- 纯右值:非引用返回的临时变量、运算表达式产生的临时变量如a+b、原始字面量和 lambda 表达式等
- 将亡值:与右值引用相关的表达式、返回T&& 类型函数的返回值
1.2 右值引用
常见的 & 为左值引用、右值引用使用 && 表示
int&& a = 123; int &b = a; int &&c = a;//不合法
如上 a 是对123的右值引用,但是a本身是左值,其在内存中有明确的存储地址,所以c不能再对其进行左值引用。
1.3 右值引用的意义
可以将资源(堆、系统对象等)通过浅拷贝从一个对象转移到另一个对象这样就能减少不必要的临时对象的创建、拷贝以及销毁,可以大幅提高应用程序的性能。
#include <iostream> using namespace std; class Test { public: Test() : num(new int(100)) { cout << "construct" << endl; } Test(const Test& a) : num(new int(*a.num)) { cout << "copy construct" << endl; } // 移动构造函数其实就是将入参的资源赋值给自己,并将入参的对应资源指针制空, Test(Test&& a) : num(a.num) { cout << "rv copy construct" << endl; a.num = nullptr; } ~Test() { delete num; } int* num; }; Test getObj() { Test t; return t; } int main() { Test t = getObj(); return 0; };
getObj()会得到一个非引用的临时对象,是纯右值,如果只有拷贝构造函数就只能再次new一块区域去保存该右值的资源,这是因为不能确定拷贝构造传入的参数后面是不是还会继续被使用, 只好进行深拷贝。而对于传入右值的情况,可以确定右值以后不会再进行访问,因此可直接将其指针复给新对象,将入参的对应指针置为null,防止析构造成野指针,避免深拷贝带来的性能消耗。
由此可见,右值引用具有移动语义:将确定后续不再使用的对象中的资源转移给新的对象,虽然左值引用也能够做到资源转移,但传入的左值后续可能还会被更改和使用,个人认为右值引用恰好做到了这种区分。
2、move
使用std::move方法可以将左值转换为右值。使用这个函数并不能移动任何东西,而是和移动构造函数一样都具有移动语义,将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存拷贝。
当确定一个变量a后续不会再进行使用,并且需要将其赋值给另一个对象时,可以使用移动构造来转移资源
Test a;
Test && b = a; // error
上面的操作是不可行的,因为a不是一个右值,要想调用Test的移动构造函数,就必须将a这个左值转变为一个右值:使用move() 函数
Test a;
Test && b = move(a); // ok
std::move 基本等同于一个类型转换:
static_cast<T&&>(lvalue)
3、foward
move将左值转换为右值,foward可以满足更多的情形
std::forward<T>(t);
- 当T为左值引用类型时,t将被转换为T类型的左值
- 当T不是左值引用类型时,t将被转换为T类型的右值
int a = 123;
foward<int&>(a); // a转换为左值并返回
foward<int&&>(a); // a转换为右值并返回
foward<int>(a); // a转换为右值并返回
加载全部内容