C++中的通俗理解左值,右值,左值引用,右值引用

背景

在C++11中添加了几个概念:左值,右值,左值引用,右值引用。这几个概念非常抽象,只能尽力去解释和阐述。

左值

左值 = 内存 + 名字 + 地址
在这里插入图片描述

int a = 1 //a是左值,因为有名字a, 内存里面是1,地址是ox01
a = 2; //左值
printf("%d\n", a = 3);//利用了左值的右值属性
右值

左值 = 内存 + 地址
在这里插入图片描述

int a = 1 //1是右值,因为只有内存+地址,没有名字

左值引用

给有名字的内存额外起一个别名,需要用语法&
在这里插入图片描述

int a = 1;
int& b = a;

右值引用

给没名字的内存(右值)起一个名字,需要用语法&&

int&& a = 1;

在这里插入图片描述

可以发现,一旦给右值起了一个名字,就会发现右值变成了左值(名字+内存+地址),这个要好好理解。

const引用

在这里插入图片描述

const int& a = 3;

我们知道3是右值,而&是给一个有名字的内存(左值)取一个别名, 为了能给右值起别名,看清楚是起别名,不是起名字,因为起名字用&&就可以。这里可以通过const加一个限制,那么就可以给3起别名了,尽管3就一个名字。留一个问题,现在的a是左值还是右值?(答案是左值,因为满足了左值条件)
const 引用这种哪里可以用到?等到大家有机会写多线程代码的时候就会遇到,在创建thread对象的时候,参数会被拷贝到新的线程环境中,届时会以一个右值的形式传递给线程函数,而线程函数的形参如果是普通引用就会报错,加上const的话就没有问题。

测试

既然我们已经学完1+1=2了,那么下面开始来解一元二次方程了!

demo_1
int& a = 3;

会编译报错,为什么?因为3是一个右值,而&是给左值起一个额外的名字,所以编译报错。

demo_2
int a = 4;
int &&b = a;

会编译报错,为什么?因为a是一个左值,而&&是给右值起第一个名字,你都有名字了还来用&&凑热闹,所以编译报错。

demo_3
int a = 4;
int &b = a * 2;

会编译报错,为什么?因为a是一个左值,a *2 这里是利用了a的右值属性,所以a *2 会生成一个没有名字的右值,你去给一个没有名字的内存块取别名是不行的,因为他都没有名字,何来别名一说?所以你只能用&&给他取一个名字

move语义

move的意思是把左值的名字移出掉(其实还是可以用a去调用),所以会变成右值,因此一个左值一旦用了move, 最好保证它在move后不要再被调用,毕竟move的思想就是把名字除去了,毕竟都被除名了,也就是想表达以后我不会再叫你a了。
在这里插入图片描述

#include <iostream>
using namespace std;

void Show(int&& para)
{
        cout<<para<<endl;
        para = 4;
}

int main()
{
        int a = 5;
        Show(3);//right
        Show(a);//error
        Show(move(a));//right
        cout<<a<<endl;//最好杜绝这样用法,可以看到a已经变成4了
        return 0;
}

注意

  • 右值引用传参
  1. 比如foo(T&& par), 虽然par是右值引用类型,但是一旦进入函数里面就是左值了,因为上面讲过了,给一个右值起一个名字后就是左值了,这个要牢记,因为以后玩forward的话就是需要这个特性。
  2. 凡是引用,不管是左值引用还是右值引用,凡是进到函数里面都不会被析构,因为底层就是传指针进去,不涉及构造,当然也就没有析构。
  • move语法
  1. 一直以来,很多人都认为move后的对象不能用了,比如下面的示例,所以很多人就误传move后的值不能用了,其实这是一个大大的错误。
string value = "test";
cout<<"before="<<value<<std::endl; //输出test
string tmp(std::move(value));
cout<<"after="<<value<<std::endl; // 输出空值

接下来再看一个代码:

void foo(string&& value) {};
string value = "test";
cout<<"before="<<value<<std::endl; //输出test
foo(std::move(value));
cout<<"after="<<value<<std::endl; // 输出test

可以看到上面的代码,在move后test值依然存在,这里是因为其实是string的右值copy 构造函数里面把输入置空了,这才导致第一个demo中的value在move后输出为空,但是第二demo中foo函数没有操作value,所以value的值还在没有任何变化,而且根据传引用不会析构参数,所以原始value这个对象一点都没变,既没有消失也没有析构,我们常用的unique_ptr就是这样的,不会让原来的对象消失。string的之所以为空是因为string(string&& parm)函数里面自己捣的鬼。
2. 所以我们把move当成reinterpret_cast<T&&>就行了,这个是在编译期就决定的,跟运行时没有任何关系

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值