左值、右值、左值引用,右值引用,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. 左值和右值总结说明