C++11---右值引用(深度讲解)

简要介绍

右值引用是C++11的新特性,无论左值引用还是右值引用,都是在给对象取别名

什么是左值 什么是右值

1.左值,左值引用

左值是一个数据的表达式(例如变量或者解引用后的指针),我们可以对其进行取地址和修改赋值,左值可以出现在赋值符号的左边,而右值不能出现在赋值符号的左边,而左值引用就是对左值起别名,

如图

2.右值

右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引
用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能
取地址

右值引用

右值引用就是对右值的引用,给右值取别名

这几个都是常见的右值引用

需要注意的是,在给右值引用后,变量就会变成左值,为什么? 因为需要对其进行修改,如果需要再要将其变成右值,需要加上std::move()将其变成右值

右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可
以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是a引用后,可以对a取地
址,也可以修改rr1。如果不想a被修改,可以用const int&& a 去引用,是不是感觉很神奇,
这个了解一下实际中右值引用的使用场景并不在于此,这个特性也不重要

 左值引用和右值引用比较

左值引用总结:

1. 左值引用只能引用左值,不能引用右值。
2. 但是const左值引用既可引用左值,也可引用右值


右值引用总结:

1. 右值引用只能右值,不能引用左值。
2. 但是右值引用可以move以后的左值。

为什么要有右值引用

从左值引用窥探

前面我们可以看到左值引用既可以引用左值和又可以引用右值,那为什么C++11还要提出右值引
用呢?是不是画蛇添足呢?下面我们来看看左值引用的短板,右值引用是如何补齐这个短板的!
注意以下采用右值引用的移动构造

#pragma once
class string
{
public:string(const char* str = ""):_size(strlen(str)), _capacity(_size){//cout << "string(char* str)" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s):_str(nullptr){cout << "string(const string& s) -- 深拷贝" << endl;string tmp(s._str);swap(tmp);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}// 移动构造string(string&& s):_str(nullptr), _size(0), _capacity(0){cout << "string(string&& s) -- 移动语义" << endl;swap(s);}// 移动赋值string& operator=(string&& s){cout << "string& operator=(string&& s) -- 移动语义" << endl;swap(s);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}//string operator+=(char ch)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}
private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0
};

左值引用的使用场景:
做参数和做返回值都可以提高效率

如下:

void func1(string s1)
{}
void func2(string& s1)
{}
int main()
{string s1("hello world");// func1和func2的调用我们可以看到左值引用做参数减少了拷贝,提高效率的使用场景和价值func1(s1);func2(s1);// string operator+=(char ch) 传值返回存在深拷贝// string& operator+=(char ch) 传左值引用没有拷贝提高了效率s1 += '!';return 0;
}

左值引用的短板:
但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回
只能传值返回。例如:string to_string(int value)函数中可以看到,这里只能使用传值返回,
传值返回会导致至少1次拷贝构造(如果是一些旧一点的编译器可能是两次拷贝构造)。

 这里可以看到,传值返回会至少导致一次拷贝构造(旧编译可能优化没有那么好就是两次了)

因为to_string的返回值是右值,利用这个右值构造ret,如果没有了移动构造,就会去调用拷贝构造,就会给上一个深拷贝,可以看见,这里是有优化空间的

出来吧!我的右值引用和移动语义

解决方案:

在string中增加移动构造,移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不
用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己

string (string&& s):_str(nullptr)
{cout<<"这是移动构造"<<endl;swap(s);
}
string (const string& s):_str(nullptr)
{cout<<"拷贝构造深拷贝"<<endl;string tmp._str;swap(tmp,s);
}
int main()
{string ret2 = bit::to_string(-1234);return 0;
}

 再运行一下,就会发现你会调用移动构造,而移动构造里没有开空间,进行拷贝数据,所以效率提高了

(当移动构造和拷贝构造同时出现的时候,因为to_string的返回值是右值,所以编译器就会选择适合的->移动构造)

同样的道理,移动赋值也是这样

string& operator=(string&& s)
{cout << "string& operator=(string&& s) -- 移动语义" << endl;swap(s);return *this;
}
int main()
{string ret1;ret1 = to_string(1234);return 0;
}

// 运行结果:
// string(string&& s) -- 这是移动构造
// string& operator=(string&& s) -- 移动语义

 这里运行后,我们看到调用了一次移动构造和一次移动赋值。因为如果是用一个已经存在的对象
接收,编译器就没办法优化了,to_string函数中会先用str生成构造生成一个临时对象,但是
我们可以看到,编译器很聪明的在这里把str识别成了右值,调用了移动构造。然后在把这个临时
对象做为to_string函数调用的返回值赋值给ret1,这里调用的移动赋值

万能引用 完美转发

模板中的万能引用

void Fun(int &x){ cout << "左值引用" << endl; }
void Fun(const int &x){ cout << "const 左值引用" << endl; }
void Fun(int &&x){ cout << "右值引用" << endl; }
void Fun(const int &&x){ cout << "const 右值引用" << endl; }
// 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
// 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,
// 但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,
// 我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发
template<typename T>
void PerfectForward(T&& t)//万能引用 传左值的时候就变成 int& t 右值就是int&&
{Fun(t);
}
int main()
{PerfectForward(10); // 右值int a;PerfectForward(a); // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b); // const 左值PerfectForward(std::move(b)); // const 右值return 0;
}

std::forward完美转发

forward完美转发可以在传参的时候保留对象原生类属性

void Fun(int &x){ cout << "左值引用" << endl; }
void Fun(const int &x){ cout << "const 左值引用" << endl; }
void Fun(int &&x){ cout << "右值引用" << endl; }
void Fun(const int &&x){ cout << "const 右值引用" << endl; }
// std::forward<T>(t)在传参的过程中保持了t的原生类型属性。
template<typename T>
void PerfectForward(T&& t)
{Fun(std::forward<T>(t));
}
int main()
{PerfectForward(10); // 右值int a;PerfectForward(a); // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b); // const 左值PerfectForward(std::move(b)); // const 右值return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/788240.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言 练习题

目录 1.统计二进制中1的个数 方法1 方法2 方法3 2.求两个数二进制中不同位的个数 方法1 方法2 3.打印整数二进制的奇数位和偶数位 4.用“ * ”组成的X形图案 5.根据年份和月份判断天数 6.结语 1.统计二进制中1的个数 【题目内容】 写一个函数返回参数二进制中 1 的个…

YARN集群 和 MapReduce 原理及应用

YARN集群模式 本文内容需要基于 Hadoop 集群搭建完成的基础上来实现 如果没有搭建&#xff0c;请先按上一篇: <Linux 系统 CentOS7 上搭建 Hadoop HDFS集群详细步骤> 搭建&#xff1a;https://mp.weixin.qq.com/s/zPYsUexHKsdFax2XeyRdnA 配置hadoop安装目录下的 etc…

web-AOP

AOP基础 AOP进阶 通知顺序和类型 切入点表达式 连接点

任意设定蜂鸣器响的次数

这次来分享一个比较有意思的知识点 控制蜂鸣器响的次数 首先&#xff0c;我自己画了一个蜂鸣器的小模块&#xff0c;用来测试的。如下图 实物和原理图如上图 下面是代码解释

回顾快速排序

快速排序 快速排序的核心&#xff1a; 找到一个key 通常左边的数比key小&#xff0c;右边的数比key大。 找key通常有三种方法&#xff1a; 1. 挖坑法&#xff1a; 代码实现&#xff1a; // int _pivot(int* a, int left, int right) {int begin left, end right;int in…

既然有 HTTP 协议,为什么还要有 WebSocket?(计算机网络)

看起来服务器主动发消息给客户端的场景&#xff0c;是怎么做到的&#xff1f; 使用 HTTP 不断轮询 怎么样才能在用户不做任何操作的情况下&#xff0c;网页能收到消息并发生变更。 最常见的解决方案是&#xff0c;网页的前端代码里不断定时发 HTTP 请求到服务器&#xff0c;服…

联系媒体要有方法莫让投稿发文章只剩一声长叹相见恨晚

曾有一位饱经世事的前辈以一句至理名言警醒世人:“人之所以领悟道理,往往不是源于抽象的道理本身,而是生活给予的实实在在的挫折教训,如同撞南墙一般的痛彻觉醒;同样,让人豁然开朗的,也不是空洞的说教,而是实实在在的人生磨砺。”这一哲理,放在我们日常工作中亦有深刻的启示作用…

DHCP原理重磅来袭——走过路过不要错过

目录 一.DHCP来源 &#xff08;1)手工分配缺点 (2)DHCP优点 二.DHCP设备调试 &#xff08;1&#xff09;.基本配置&#xff1a; &#xff08;2&#xff09;接口地址池 1.开启DHCP功能 2.开启DHCP接口地址池功能 3.查看IP地址分配结果 &#xff08;3&#xff09;全局地…

Grafana实时监控minio的极简方法

背景 想监控一下minio的部分信息. 使用过程中需要关注的内容挺多的. 只看简单的node感觉已经不够了. 所以想监控易一下. ERLANG 复制 全屏 方式和方法 minio其实集成了prometheus 支持的监控指标 只需要在配置文件中放开就可以了. 虽然可以使用mc 的命令 create beartoken 但…

每日一题:c语言实现n的阶乘

目录 一、要求 二、代码 三、结果 一、要求 实现n的阶乘&#xff0c;已知n&#xff01;1*2*3*…*n 二、代码 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h>int main() {//初始化变量n为要求的几阶&#xff0c;jiecheng存储结果的&#xff0c;初始化为1…

AWTK 开源串口屏开发(15) - 通过 MODBUS 访问远程设备数据

在 AWTK 串口屏中&#xff0c;内置了 MODBUS Client 的模型&#xff0c;支持用 MODBUS 协议从远程设备获取数据。不用编写一行代码即可实现对远程设备数据的显示和修改。 1. 功能 不用编写代码&#xff0c;实现对远程设备数据的显示和修改。 2. 创建项目 从模板创建项目&am…

UGUI 进阶

UI事件监听接口 目前所有的控件都只提供了常用的事件监听列表 如果想做一些类似长按&#xff0c;双击&#xff0c;拖拽等功能是无法制作的 或者想让Image和Text&#xff0c;RawImage三大基础控件能够响应玩家输入也是无法制作的 而事件接口就是用来处理类似问题 让所有控件都…

【愚公系列】2024年03月 《AI智能化办公:ChatGPT使用方法与技巧从入门到精通》 015-用 ChatGPT 生成图片(Midjourney生图方法)

🏆 作者简介,愚公搬代码 🏆《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,51CTO博客专家等。 🏆《近期荣誉》:2022…

无人机+AI平安校园系统在天津大学成功运行

8月17日&#xff0c;北方天途航空联合天津大学&#xff0c;运用天途平安校园管理平台和大疆无人机场&#xff0c;开展无人机智能监控试点任务&#xff0c;协助学校保卫处监控人员和车辆情况&#xff0c;进行安全疏导和分流。 观看天途平安校园系统演示 现场工程师把天途平安校园…

一起来从Solidworks中导出URDF模型

这个博客是用来记录关于【从Solidworks中导出URDF模型】的学习历程&#xff1a; 相关课程链接见&#xff1a;如何从Solidworks导出URDF模型 • 古月 (guyuehome.com) 下面让我们一起开始吧&#xff01;&#xff01;&#xff01; 1. sw_urdf_exporter插件介绍 插件下载链接&…

2009-2023年上市公司华证ESG评级数据

2009-2023年上市公司华证ESG评级数据 1、时间&#xff1a;2009-2023年 2、指标&#xff1a;代码、名称 、华证ESG评级 3、来源&#xff1a;整理自wind 4、范围&#xff1a;上市公司 5、指标解释&#xff1a; 华证ESG评级是一种评估企业环境、社会和治理绩效的评级体系。…

搜维尔科技:SenseGlove Nova 允许以最简单的方式操作机器人并与物体交互

扩展 Robotics 和 QuarkXR 人机界面 XR 应用 Extend Robotics 利用扩展现实技术&#xff0c;让没有机器人专业知识的个人能够远程控制机器人。他们的 AMAS 解决方案使操作员能够不受地理限制地轻松控制机器人。 需要解决的挑战【搜维尔科技】 目前&#xff0c;操作机器人是一…

初学者必看!bashplotlib库让你轻松在Bash脚本中实现数据可视化

1. 是什么 bashplotlib 是一个 Python 库&#xff0c;用于在 Bash 脚本中生成数据可视化。它允许用户使用 Python 代码创建各种类型的图表&#xff0c;并将它们嵌入到 Bash 脚本中。bashplotlib 支持多种图表类型&#xff0c;包括条形图、折线图、饼图等。 2. 核心功能 bashplo…

Cookie 和 Session

1. 回顾 Cookie是浏览器在本地持久化存储结构的一种机制. 1.1 Cookie的数据从哪里来? 服务器返回给浏览器的. 1.2 Cookie的数据是什么样的? Cookie的数据是键值对结构. 并且这里的键值对都是程序员自定义的. 1.3 Cookie的作用是什么? Cookie可以在浏览器这边存储一些…

mysql 数据库基本操作

mysql 数据库基本操作 1、创建五张表 – user 表&#xff1a;后台用户表 – product 表&#xff1a;产品表 – account 表&#xff1a;客户账户表 – product_account 表 : 客户购买表 – customer 表 &#xff1a; 客户表 2、创建表 SQL 语句&#xff1a; 注意&#xff1a…