赞
踩
int i = 10; //对象:一块内存区域
i = 20;
左值:能用在赋值语句等号左侧的东西,它能够代表一个地址。
右值:不能作为左值的就是右值,右值不能出现在赋值语句中等号的左侧。
结论:C++的一条表达式,要么是右值,要么是左值,不可能两者都不是。但是左值有的时候能被当做右值使用。
int i = i + 1; // i 是个左值,不是个右值,虽然它出现在了等号的右边。i 用在等号右边的时候,我们说 i 有一种右值属性(不是右值)。i 出现在等号左边,用的是i代表的内存中的地址,我们说 i 有一种左值属性。
一个左值,它可能同时具有左值属性和右值属性。
用到左值得运算符有哪些:
<1>赋值运算符
int a;
printf(“%d\n”, a = 4); //4
//整个赋值语句的结果仍然是左值
(a = 4) = 8;
<2>取地址 &
int a = 5; //变量就是个左值
&a; &8;
<3>string,vector 下标 [] 都需要左值,迭代器
string abc = “I Love China”;
abc[0];
vector::iterator iter;
…
iter++; iter–;
<4>通过看一个运算符在一个字面值上能不能操作,我们就可以判断运算符是否用到的是左值
i++; //可以
5++; //不可以
左值表达式就是左值,右值表达式就是右值。
左值:代表一个地址,所以左值表达式的求值结果,就是一个对象,就得有地址。 反过来不成立,求值结果为对象的表达式不一定是左值。
求值结果为对象的表达式,不代表一定是左值,具体再分析。
int value = 10;
int& refval = value;
refval = 13; //value = 13;
三种形式的引用
<1>左值引用(绑定到左值)
<2> const 引用(常量引用),也是左值引用,我们不希望改变值的对象。
int value = 3;
const int &ref = value;
ref = 4; //错,编译器提示:表达式必须是可修改的左值
<3>右值引用(绑定到右值):它是个引用
int&& refrightvalue = 3;
refrightvalue = 4;
三:左值引用
引用左值,绑定到左值上。
char *p = nullptr; //指针有空指针的说法。
没有空引用的说法,所以左值引用初始化的时候就绑定左值。
int a = 1;
int &b{ a }; //b 绑定到 a
int &c; //错误引用必须初始化
int &c = 1; //不可以,左值引用不能绑定到右值上,必须绑定左值。
const int &c = 1; //const 引用可以绑定到右值上,所以const引用特殊,相当于如下语句
// int temp = 1;
// const int &c = temp;
四:右值引用
就是引用右值,也就是说绑定到右值。必须是绑定到右值的引用。
&&,希望用右值引用来绑定一些即将销毁的或者是一些临时的对象上。右值引用也是引用。
int &&refrightvalue = 3; //右值引用大家可以理解成一个对象的名字
refrightvalue = 5;
int value = 10;
int& refval = 5;
能绑定到左值上去的引用,一般都不能绑定到右值。
int value = 10;
int&& refrightvalue = value; //右值引用也绑不到左值上
string strtest{ “I love China!” };
string &r1{ strtest }; //可以,左值引用绑定左值
string &r2{ “I love China!” }; //不可以,左值引用不能绑定到临时变量。临时变量被系统当做右值
const string &r3{ “I love China!” }; //可以,创建个临时变量,绑定到左值r3上去
const 引用不但可以绑定右值,还可以执行到 string 的隐式类型转换,并将所得到的值放到 string 临时变量中
string &&r4{ strtest }; //不可以,右值引用不能绑定到左值
string &&r5{ “I love China” }; //可以,绑定到一个临时变量,临时变量的内容 I love China
int i = 10;
int& r1 = i; //正确,左值引用
int&& r2 = i; //不可以,不能将右值引用绑定到左值上
int&& r3 = i * 100; //可以,右值引用绑定到右值
int &r4 = i * 100; //不可以,左值引用不能绑定到右值
const int &r4 = i * 100; //绑定到右值
int&& r5 = i * 1000; //可以,右值引用绑定到右值
总结:返回左值引用的函数连同赋值,下标,解引用和前置递增递减运算符(–i),都是返回左值表达式的例子;
我们可以将一个左值引用绑定到这类表达式上,返回非引用类型的函数,连同算术,关系,位以及后置递增递减运算符(i–),都生成右值。
不能将一个左值引用绑定到这类表达式上,但是我们可以将一个const的左值引用或者一个右值引用绑定到这类表达式上。
++i; //左值表达式。 ++i 直接给变量 i+1,然后返回 i 本身
因为 i 是变量,所以可以被赋值。
int i = 100;
(++i) = 199; //i 被赋值成199了。
i++; //先用后加,为什么是右值表达式呢?
i++ 先产生一个临时变量 temp 记录 i(temp = i) 的值用于使用目的,再给 i+1,接着返回这个临时变量,
临时变量这个东西右值 (i++) = 199; //语法错误。
int i = 1;
int &&r1 = i++; //成功绑定右值。但是此后r1的值和 i 没有关系
i += 5;
int& r2 = i++; //不可以,不能绑到右值表达式上去
int& r3 = ++i; //r3 绑到 i,r3 就变成i的别名了
i += 5;
int&& r4 = ++i; //不可以,右值不能绑定左值表达式
重点强调:
<1>r1 虽然是右值引用(绑定到了右值),但是 r1 本身是左值(把r1看成一个变量),因为他在=左边待着
int& r5 = r1; //r1是左值
int&& r6 = r1;
<2>所有的变量,看成左值,因为他们是有地址的。而且你用右值引用也绑定不上。
int&& r11 = i; //失败
<3>任何函数里面的形参都是左值,void f(int i,int &&w) w 是右值引用,但是w本身是左值
<4>临时对象都是右值。
右值引用的目的
<1>C++11引入,&&,代表一种新的数据类型,引入新数据类型肯定有目的。
<2>提高程序运行效率,把拷贝对象变成移动对象来提高程序运行效率。
<3>移动对象如何发生。&&(应付移动构造函数,移动赋值运算符)
五:std::move函数
C++标准库里面的新函数
std::move:移动。实际上这个函数根本就没有做移动的操作。
std::move 的能力只有一个,就是把一个左值强制转换成一个右值。带来的结果就是一个右值可以绑上去了;
int i;
int &&ri20 = std::move(i); //把一个左值转成一个右值,这就是move的能力。
i = 20;
ri20 = 15; //ri20 就代表 i 了。
int&& ri6 = 100;
//int&& ri8 = ri6;
int&& ri8 = std::move(ri6); //绑定
ri6 = 68;
ri8 = 46;
string st = “I love China!”;
const char* p = st.c_str();
string def = std::move(st); //string里的移动构造函数把st的内容转移到了def中去了。而不是std::move(); 触发了string的移动构造函数
const char* q = def.c_str();
std::move(st);
string&& def = std::move(st);
//st = “abcd”;
def = “defghijh”;
六:左值右值引用总结说明
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。