左值、右值、左值引用,右值引用,std::move函数

左值、右值、左值引用,右值引用,std::move函数

1. 左值和右值

 int i = 10; //  对象:一块内存区域
 i = 20;
 //  左值:能用在赋值语句等号左侧的东西,它能够代表 一个地址
 //  右值:不能作为左值的就是右值

 //  结论:C++ 的一条表达式,要么就是左值,要么就是右值,不可能两者都不是
 //  左值有时候能够被当做右值使用
 i = i + 1;  //  因为已经出现在左侧了,所以是左值【i 用在等号右边的时候,我们说 i 有一种右值属性(不是右值)】

 //  用到左值的运算符有哪些:
 //  a) 赋值运算符
 int a;
 printf("%d\n", a = 4);  //  整个赋值语句的结果仍然是左值(即:一个内存地址)
 (a = 4) = 8;
 //  b) 取地址 &
 int b = 5;
 int* p = &b;    //  指针变量也是变量(所以也是左值)

 //  c) string, vector 下标[] 都需要左值
 string abc = "I love You";
 vector<int>::iterator iter;
 abc[0];
 iter++;
 iter--;

 //  d) 通过看一个运算符在一个字面值上能不能操作,我们就可以判断运算符是否用到的是左值
 //  i++,5++

 //  左值表达式【左值】、右值表达式【右值】
 //  左值:代表一个地址,所以左值表达式的求值结果,就得是一个对象。

2. 引用【变量的别名:美少女战士】分类

//  3 种形式
int value = 10;
//  1)左值引用(绑定到左值) 
int& refval = value;
refval = 13;    //  等价于 value = 13;
//  2)const引用(常量引用),也是左值引用的一种,我们不希望改变值的对象
const int& refval2 = value;
//  3)右值引用(绑定到右值【临时的、使用完不需要保留的】)
int&& refRightValue = 3;
refRightValue = 5;

2.1 左值引用

 //  左值引用
 //  引用左值
 char* p = nullptr;  //  指针有空指针的说法
 //  但是引用没有空引用的说法,所以左值引用初始化的时候就绑定左值
 int a = 1;
 int& b = a;
 //  int& c =1;  不可以,左值引用不能绑定到右值,必须绑定到左值
 const int& c = 1;   //  const引用可以绑定到右值,所以 const 引用特殊
 //  原因:const 引用等价于2条语句
 int tempValue = 1;
 const int& = tempValue;

2.2 右值引用

//  右值引用:就是引用右值,也就是说,绑定到右值
//  必须是绑定到右值的引用
//  &&:希望用右值引用绑定一些即将销毁的或者一些临时的对象上
//  注意:右值引用 也是引用。
int&& refRightValue = 3;    //  右值引用大家理解成一个对象的名字

string str = "I love You";
string& strRef1 = str;   //  可以,左值引用绑定左值
// string& strRef2 = "I love You"; 不可以,左值引用 不能绑定到临时变量,临时变量被系统当作右值
const string* strRef3 = "I love You";   //  可以,相当于创建一个临时变量,绑定到左值引用 strRef3 上  
//  const 引用不仅可以绑定到右值,还可以执行到 string 隐式类型转换,并将所得到的值放到 string 临时变量中

stirng&& strRef4 = "I love You";    //  可以,绑定到一个临时变量,临时变量内容 I love China

//  --i;返回左值的表达式
//  i--; 返回右值的表达式
int i = 1;
int&& r1 = i++; //  成功绑定右值(i++ 所返回的临时变量,而不是 i 本身),但是此后 r1 和 i 没有关系

//  重点强调:r1 虽然是右值引用(绑定了右值),但是 r1 本身是左值(你要把 r1 看作是一个变量),因为它在等号左边
//  证明:r1 是左值
int& r5 = r1;   //  r1 是左值

//  注意:任何函数里边的形参都是左值,void function(int&& w) ===> w 虽然是右值引用,但是 w本身是左值【因为在等号左侧】

//  临时对象都是右值

右值引用引入的目的(提高系统运行效率)

 //  右值引用的引入目的
 //  a)C++11 引入,&&,代表一种新型数据类型,引入新数据类型肯定有目的
 //  b)提高程序运行效率:把拷贝对象变成移动对象
 //  c)移动对象如何发生:&&(应对移动构造函数,应对移动赋值运算符)

3. std::move 函数

//  std::move 函数
//  C++ 标准库里的新函数
//  std:move:移动(把一个左值 强制转换成一个右值)===> 带来的结果就是:我一个右值可以绑上去了
int i = 10;
int&& refi = std::move(i);  //  把一个左值 转成一个右值,这就是 move 的能力
i = 20;
refi = 88;  //  refi 就代表 i 了

string st = "I love you";   
const char* p = st.c_str();
printf("p: %p\n", p);
//  def:一个新的对象,会调用 string 里的移动构造函数
string def = std::move(st); //  执行过后,st 变为空 ===> string 里的移动构造函数把 st 的内容转移到了 def 中去了,而不是 std::move() 干的
const char* q = def.c_str();
printf("q: %p\n", q);
//  p:000000000064fd60
//  q:000000000064fd40
//  可以看到 执行完 std::move() 后并没有节省内存

string&& def2 = std::move(st);  //  因为没有执行  string 里的移动构造函数,所以 st 里面内容没有变
st = "apt"; //  使用完 std::move() 后,系统不推荐再修改 st 了
def2 = "abc";

4. 左值和右值总结说明

posted @ 2024-11-09 00:27  爱新觉罗LQ  阅读(22)  评论(0编辑  收藏  举报