左值、右值、左值引用、右值引用
一、左值、右值
1、概念
左值:能又在赋值等号左侧的东西,它能够代表一个地址
右值:不能出现在赋值等号的左侧
注意:C++中一个表达是,要么是左值,要么是右值,不可能两者都是,或者两者都不是
只是有时候,左值可以被当作右值使用
i = i + 1; // i是左值,不是右值,虽然出现在了等号右边
// i出现在等号左边,用的是i代表的内存地址,i有左值属性
// i出现在等号右边的时候,我们称i有右值属性(但不是右值)
// 左值属性:当作一个地址
// 右值属性:当作一个值
// 一个左值可能同时具有左值属性和右值属性
2、用到左值的运算符
(1)赋值运算符
// 赋值运算语句的结果仍为左值 (a = 4) = 8; // 最终的结果是a = 8,说明a = 4的结果仍为一个左值,仍可以被赋值
(2)取地址 &
int a = 5; // 注意:!!变量都是左值 &a; // 只有左值才能被取地址
(3)string, vector 下表[] 都需要左值
string str = "Hello"; str[0]; // str 必须为左值才可以
二、左值引用、右值引用
1、概念
左值引用:绑定到左值上
没有空引用的说法,左值引用初始化的时候就要绑定左值
右值引用:绑定到右值上
希望用右值来绑定一些即将销毁的或者是临时的对象
int a = 1; // int &b = a; // 正确,绑定到左值 const int & c = 1; // 正确,const引用可以绑定到右值上 int&& e = 1; // 正确,右值引用
// 注意:绑定之后的e 和原来的1 没有关系,e 绑定的是一个临时变量
int & = 1; // 错误,左值引用不能绑定右值 int i = 10; int & r = i * 100; // 错误 int && r1 = i * 100; // 正确 const int & r2 = i * 100 // 正确 // 能绑定到左值的引用,一般都不能绑定到右值 // 能绑定到右值的引用,也不能绑定到左值
变量都是左值,临时变量被系统当作右值
++i; // 返回的是引用 { i = i + 1; return i; } i++; // 返回的右值 { temp = i; i = i + 1; return temp; }
三、重点强调
int && r1 = 5; //(1) r1虽然是右值引用,但是r1本身它是左值(把r1看做一个变量)。 int &r2 = r1; // 说明r1 是左值,不能用右值引用绑定 int &&r3 = r1; // 错误 //(2)所有变量都看成是左值,因为都是有地址的,且不能用右值引用绑定 //(3)任何函数的形参都是左值 //(4)临时对象都是右值 //(5)引入右值引用的目的: //(5.1)提高程序运行效率,把“拷贝对象”变成“移动对象”来提高程序运行效率 //(5.2)移动拷贝构造函数,和移动赋值运算符,参数类型是 &&,所以引入&&来满足移动拷贝,和移动赋值运算符。
四、std::move()
只有一个能力,把一个左值强制转换成一个右值
int i = 10; int && r2 = std::move(i); //把一个左值转换成右值 i = 20; // r2 和 i 都是20 r2 = 15; // r2 和 i 都是15 // 说明用std::move() 强转之后,r2就代表 i 了,两个变量成了同一个值 int && r3 = r2; // 错误 int && r4 = std::move(r2); // 将r2 和r4 绑定到一起了,两个的值会一起变化 // 注意 string str = "Hello"; string str2 = std::move(str); //执行完这句话之后,str2 的值变成了"Hello",而str的值变成了空 std::move(str); // 只执行这句话,str是不会为空的,因为置空的操作是在移动构造函数中进行的,没有用变量去接这个返回值,就不会执行移动构造函数 string && str3 = std::move(str); // 这样操作,不会触发移动构造函数,只是将str3 绑定到str 上,两个变量相同 // 这是因为这种对象(包括自定义对象,只要不是系统内置的变量类型),会执行移动构造函数,而在移动构造函数里面,将str2的值变成“Hello”,将str的值置空 // 一般来说,移动构造函数,是将str2指向str原来的内存,而将str置空,这样就不需要新开辟内存,拷贝的动作 // 但是由于内存的限制,string的移动构造函数,是新开辟了内存空间拷贝了原来的字符串,所string的移动构造 函数并没有比拷贝构造函数节省时间
小结:一般来说,执行std::move(str); 后面就不要再用str了。