转载

【原创】深入理解c++的右值引用

0 左值和右值

一个左值表达式代表的是 对象本身 ,而右值表达式代表的是 对象的值 ;变量也是左值。

1 右值引用作用

为了支持移动操作(包括 移动构造函数和移动赋值函数 ),C++才引入了一种新的引用类型——右值引用,可以自由接管右值引用的对象内容。

2 右值引用绑定的对象

返回非引用类型的函数,产生右值的表达式(算术表达式、关系表达式、位、后置递增递减)

3 和左值引用的区别

  1. 绑定的对象(引用的对象)不同,左值引用绑定的是返回左值引用的函数、赋值、下标、解引用、前置递增递减
  2. 左值持久,右值短暂,右值只能绑定到 临时对象 ,所引用的对象将要销毁或该对象没有其他用户
  3. 使用右值引用的代码可以自由的接管所引用对象的内容

清单1

1 int &&i = 1; 2 int b = 2; 3 cout << i << endl; 4 i = b; 5 cout << i << endl; 6 //输出1 2

清单1中i绑定到了右值1,说明:初始化时,右值引用一定要用一个右值表达式绑定;初始化之后,可以用左值表达式修改右值引用的所引用临时对象的值

清单2:清单1反汇编

;省略上面代码  int &&i = 1; 00C044DE  mov   dword ptr [ebp-14h],1   00C044E5  lea   eax,[ebp-14h]   00C044E8  mov   dword ptr [ebp-8],eax    int b = 2; 00C044EB  mov   dword ptr [ebp-20h],2   ;省略输出代码  i = b; 00C04523  mov   eax,dword ptr [ebp-8]   00C04526  mov   ecx,dword ptr [ebp-20h]   00C04529  mov   dword ptr [eax],ecx   ;省略余下代码 

清单2可以清楚看出右值引用的底层实现。第3行对一个临时变量赋值0,第4-5行将临时变量地址存储至i中,因此i存储的实际是临时对象的地址(也就是对临时对象的一种引用)。

第10行mov  eax,dword ptr [ebp-8]取出了i存储的数据(临时对象地址),11-12行将b变量的值赋值到临时对象,而i存储的数据不变,仍是存储临时对象的地址(也就是对临时对象的一种引用)。

清单3:右值引用变量也是左值

1 int&&temp =0; 2 int&&i = temp;

清单3中temp是一个右值引用变量,变量是左值,因此i引用temp左值是非法的!

清单4:右值引用赋值右值引用

1 int &&temp = 0; 2 int &&i = 1; 3 i = temp;

清单4是合法的,取出temp所引用临时对象的值,再赋值给i所引用临时对象的值。

清单5

1 class A{  2 public:  3     int b;  4     int a;  5     char c[256];  6 };  7 void f(A a)  8 {  9     ; 10 } 11 int main(int argc, char const *argv[]) 12 { 13     A &&a = A(); 14     f(a); 15 }

清单5是合法的,过程符合一般逻辑,即会生成a引用临时对象的一个副本,再传入副本对象的地址。如果f函数的形参是引用类型的,那么调用f函数传入的是a引用对象的地址,而不会生成副本

4 总结

  1. 右值引用,是对临时对象的一种引用,它是在初始化时完成引用的,但是右值引用不代表引用临时对象后就不能改变右值引用所引用对象的值,仍然可以在初始化后改变临时对象的值。
  2. 对于引用类型,可以用于它所引用对象类型的可以用的地方(把它当成普通变量),只不过用到的值是它所引用的对象的值,它还可以用于移动构造或赋值函数的地方。
正文到此结束
Loading...