c++左值、右值、右值引用
c++左值、右值、右值引用
前言
这一部分对于规范代码、提高安全性、加速调试等方方面面都很重要、、问就是天天在引用和const上报红;出现诸如''表达式必须是 lvalue 或 xvalue"的错误;发现这些指针远不如我浅浅的理解中的那样简单,于是对其回炉重造!
先明确左值右值的概念
粗浅理解的话,等号左边的是左值、右边的是右值。但是它还不够明确:
int one = 1;
int another = one;
第一行中,我们令one为1,one作为左值存在;当程序执行到第二行,one此时算左值还是右值呢?
所以要关注左右值到底不同在哪,事实上可以理解为左值具有地址属性,也就是&x
编译不报错则x是左值;左值关注的是地址,右值关注的是数据值。所以等号左边的只能是左值,右边的可以是左值也可以是右值。那么下面的i分别是左值还是右值?
int i = 0;
i++;
++i;
第一行,i作为有地址的量,做左值;第二行,i++是右值;第三行,++i是左值。
i++是编译器先把i赋值给一个临时变量,完成当前的程序语句后,销毁临时变量并执行i+=1;而++i是先自增,立刻返回本身后执行当前的程序语句。
int x = 1, y = 123;
y = x++; // 先y=x=1,后x+=1;
y = ++x; // 先x+=1,后y=x;
字符串和int的区别
直接看图
这里是因为编译器会单独为字符串开辟一个空间暂存,而数字不会。
静态语言中的变量
cpp是个静态语言,变量的定义就与动态语言不同:他的变量是有类型的。
也就是对于int a=1
,在cpp里a的类型是int,a存放了数值1
;而在js(动态语言)里a没有类型、js中的数值1
是int类型的。
对于cpp:
- 可以把
int a
当做一个盒子,里面放着1 - 也可以把
int a
当做一个标签,对于1这个对象,我们为它贴了一个“是int类型、名字叫a”的标签
那么对于cpp中的变量,我们要是想拿到数值1,可以直接用a
找到;而当我们想要拿到a、类型为int的a时,则需要用到左值引用(也就是我们常说的引用)。
关于左值引用
可以把它看做指针的语法糖,因为它做的就是把地址拿出来、把a单独拿出来。那么对于const int &x = 1,你认为其中的x是左值还是右值呢?如果声明y用这种方式:int &y = 1,则编译错误。因为我们提供的是y的引用、也就提供了一个地址,但是y现在刚被声明还莫得分配地址。
而对于const int &x,x也是在此时声明的,区别在所有的const都是常量。意味着虽然x还没开辟内存空间,但是它会被分配到常量中,编译器就能经过优化直接记录这个常量。
回到刚刚的问题,const int &x = 1,其中的x是左值。但是const就特殊在这里,它也可以引用右值;放到类的各种构造函数中它将发挥很大的作用
class test{
public:
test(){};
// 这里的两个const非常重要
test(const test&){};
test& operator = (const test&){ return *this; }
};
// 将返回一个临时的test对象, 返回的test是右值, c17以后会做优化则能编译成功
test create_test(){
return test();
}
int main(){
test x;
test y(x);
test z{create_test()}; // 复制构造函数
z = create_test(); // 复制赋值
}
可以看到常量引用右值还是挺有用的,但是声明为const后,我们就无法修改它的值了(要么用不安全的强制类型转换)。为了让类可以实现上述操作,就出现了右值引用。
什么是右值引用
在上面我们执行test z{ create_test() }
时,会把create_test()中返回的内容(接下来简称为X),X的结构和数值都拷贝给test z,也就是z根据X的样子新创建了一块大小一样的地址后,再把X中所有的数值拷贝一遍、贴到z的地址中。
由于拷贝了全部的内容,可能在性能上有所损耗(比如X是一个非常复杂的类)。于是出现了转移这种概念,我们可以把X的结构和数值转移给z。转移就相当于人的改名,相对克隆一个完全相同的小明,把他改名为小花会更简单。从另一个角度想,也是延长了小明本身的声明周期。这个过程中,也要用到右值引用。
int &&num = 1; // 右值引用
使用类test:
test &&one_fromRight = create_test();
这样相当于延长X(create函数返回的那个右值对象)的生命了。如果你想看看更多关于类做对象的解释:https://www.cnblogs.com/bisa/p/16641056.html
std::move
声明移动,与右值引用配套使用
为什么说c++的指针危险?
因为指针可进行运算!因为指针可进行运算!因为指针可进行运算!它的运算意味着能指向任何一个未知的地址,如果操作不当,则造成数据混乱。