左值、右值、左值引用、右值引用

一、左值、右值

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了。

 

posted @ 2020-06-11 00:03  min_zhi  阅读(732)  评论(0)    收藏  举报