review——C++中的右值引用

目录

前言

一、什么是左值、什么是右值

二、右值引用

1.右值引用与右值引用的一些性质

2.解释一下左值引用与右值应用于程序员之间的关系

3.右值引用与移动语义

4.右值引用右值后变成左值的必要性与完美转发

1.右值引用引用右值后变为左值属性的必要性

2.完美转发

Ⅰ.引用折叠(万能引用)

Ⅱ.完美转发


前言

C++中的左值引用解决了大部分的拷贝效率问题,但是还有着诸如:一个资源占用很大的局部对象返回时,会造成多次拷贝的问题,这对C++程序运行的效率,资源的使用都是较为沉重的打击,为了解决这一问题,C++11提出了右值引用并将这一概念引入到了STL中。本文就来解释一下,什么是右值引用,右值引用的优势在哪。


一、什么是左值、什么是右值

对于如下代码,a就是一个左值,0就是一个右值。具体一点左值右值最大的差异就在于:左值可以进行取地址操作,而右值不行。

int a = 0;

二、右值引用

1.右值引用与右值引用的一些性质

①左值引用只能引用左值

②const修饰的左值应用可以修饰右值

③右值引用只能修饰右值

④右值引用可以引用move的左值

⑤对右值进行右值引用会使引用褪去常性

图1        关于性质的验证

2.解释一下左值引用与右值引用与程序员之间的关系

左值引用与右值引用一样如同一个触发时的开关,程序员对左值引用的构造函数、赋值拷贝、右值应用的构造函数、赋值拷贝函数进行定义,当用户向这些重载的函数们丢入一个数据的时候,编译器站出来帮忙选择要将这个数据放入那个函数中最能够节省效率和资源。对于一个左值输入,编译器会将使用左值引用的相关函数来对对象进行字段填充,对于一个右值输入,编译器会使用右值相关的函数来对对象进行字段填充。

图2        编译器与程序员与左右值引用

3.右值引用与移动语义

右值引用的效率提升是通过对资源的剽窃来实现的,比如说一个由动态开辟的内存的对象,生命周期马上结束,这个时候我们的函数需要返回这个对象,在C++11到来前,通常是将这个对象做一次拷贝,而后将这个拷贝的对象的值在拷贝给接收返回值的变量(此处默认,不开启编译器优化功能)。假如我们在对这个局部对象返回的时候,可以直接将需要返回对象的资源指向指针重新定位到该函数外的对象,不让编译器去两次拷贝赋值,是不是就极大的提升了效率。而右值应用很大程度上是依靠移动语义来实现效率的提升的,也就是说移动语义是效率提升的具体实现。那么移动语义具体实现是什么样子的呢,请看如下代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <assert.h>namespace test
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}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){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s):_str(nullptr){std::cout << "string(const string& s) -- 拷贝构造" << std::endl;string tmp(s._str);swap(tmp);}// 赋值重载string& operator=(const string& s){std::cout << "string& operator=(string s) -- 赋值重载" << std::endl;string tmp(s);swap(tmp);return *this;}// 移动构造string(string && s):_str(nullptr), _size(0), _capacity(0){std::cout << "string(string&& s) -- 移动构造" << std::endl;swap(s);}// 移动赋值string& operator=(string && s){std::cout << "string& operator=(string&& s) -- 移动赋值" << std::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};
}int main()
{test::string a = "111";test::string b = a;test::string c;c = a;test::string d(std::move(a));test::string e;e = "111";return 0;
}

我们自己定义一个string类型(就是类似C++STL库中的string,只不过我们手动实现一个简单版的,添加打印来查看代码的走向。),其他的类成员函数我们暂且不看,我们先将目光聚焦在这样几个函数:void swap(string& s)、string(string&& s)、string& operator=(string&& s),我们按照顺序暂且依次称呼这三个函数为swap函数,移动构造函数、移动赋值函数,我们通过上述代码不难看出swap函数的实现逻辑是:将传入对象的资源换出到this指针指向的对象中,这样就是实现来对应资源的转移。需要注意到是,如果将资源换入到当前对象中,被换出资源的对象会持有当前对象的数据,也就是说被换出资源的对象会析构换入对象的资源,所以对于移动构造我们应该对指针类型都应初始化为空指针类型,来保证换出资源对象的释放资源不会出错。

图3        swap函数工作示意图

在观察移动构造和移动赋值函数,二者在实现的时候,都是使用右值引用类型作为参数输入,在具体函数实现的过程中封装了swap函数,这其实也就解释了移动语义的工作原理,是通过资源交换来实现效率提升的。运行上述代码进行测试有如下结果:

图4        代码运行结果

4.右值引用右值后变成左值的必要性与完美转发

1.右值引用引用右值后变为左值属性的必要性

图5        关于右值引用后变为左值示例

在图5中的程序中,重载了函数func1_,函数func1调用该重载函数。但是调用的结果显示调用的是左值引用的重载函数,这一点其实我们在介绍右值引用性质的时候就已经阐述,关于这一点我们需要知道的是:

右值引用右值后,必然会变为左值属性,因为只有左值才可以被修改,能被修改的对象才可以进行资源指针指向的改变,才能将资源换出/换入。 

2.完美转发

Ⅰ.引用折叠(万能引用)

引用折叠发生在对类对象使用泛型时,如下列代码所示。

template<class T>
void func1(T &&element1)
{func1_(move(element1));func1_(std::forward<T>(element1));
}
图6        引用折叠示意图

其中的折叠规则是:

当传入的是一个左值是,若程序员没有实现对应的版本时,该模板会自动推演生成一个左值版本的func1,当传入一个右值的时候,该模板会自动推演生成一个右值版本func1。 

图7        引用折叠模板推演示意图
Ⅱ.完美转发

如何才能使func1函数保持传入参数的右值属性,进而调用右值引用呢?

图8        如何令传入参数流向右值引用的重载函数

方式一:可以对传入参数使用move函数使其转变为右值

方式二:对传入参数使用完美转发。完美转发可以通过一定的方法推导出传入内容是左值还是右值,进而对下一步的数据走向做判断。

如果只从本段代码来看方式一与方式二基本没有差异,但是我们再来看另一段代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <assert.h>using namespace std;
template<class T>
void func1_(T&& element1_)
{cout << "右值引用" << endl;
}template<class T>
void func1_(T& element1_)
{cout << "左值引用" << endl;
}template<class T>
void func1(T &&element1)
{func1_(move(element1));func1_(std::forward<T>(element1));
}int main()
{int a = 0;cout << "传入左值" << endl ;func1(a);cout <<endl<<"传入右值" << endl;func1(0);return 0;
}
图9        代码执行后的结果

 当我们传入一个右值的时候,诚然,代码没有问题。但是当我们传入一个左值的时候,方式一,就不能达到我们的要求。实际上,完美转发可以说是,为了正确转发左右值属性而诞生的。

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

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

相关文章

【docker】docker 镜像仓库的管理

Docker 仓库&#xff08; Docker Registry &#xff09; 是用于存储和分发 Docker 镜像的集中式存储库。 它就像是一个大型的镜像仓库&#xff0c;开发者可以将自己创建的 Docker 镜像推送到仓库中&#xff0c;也可以从仓库中拉取所需的镜像。 Docker 仓库可以分为公共仓…

Java获取小程序码示例(三种小程序码)

首先我们可以看到官方文档上是有三种码的 获取小程序码 这里特别要注意的是第一种和第三种是有数量限制的&#xff0c;所以大家生成的时候记得保存&#xff0c;也不要一直瞎生成 还有一点要注意的是第一种和第二种是太阳码 第三种是方形码 好了直接上代码 这里要注意&#xff…

Golang | Leetcode Golang题解之第391题完美矩形

题目&#xff1a; 题解&#xff1a; func isRectangleCover(rectangles [][]int) bool {type point struct{ x, y int }area, minX, minY, maxX, maxY : 0, rectangles[0][0], rectangles[0][1], rectangles[0][2], rectangles[0][3]cnt : map[point]int{}for _, rect : range…

HTML生日蛋糕

目录 写在前面 完整代码 代码分析 系列文章 写在最后 写在前面 HTML实现的生日蛋糕来喽&#xff0c;小编亲测&#xff0c;发给好友可以直接打开哦。在代码的第183行可以写下对朋友的祝福&#xff0c;快拿去送给你的好朋友吧&#xff01; 完整代码 <!DOCTYPE html>…

C++ 上位软件通过Snap7开源库访问西门子S7-1200/S7-1500数据块的方法

C 上位软件通过Snap7开源库访问西门子S7-1200/S7-1500数据块的方法

【LeetCode】15.三数之和

题目要求 解题思路 这道题我们可以使用暴力解法来解决&#xff0c;时间复杂度为O&#xff08;N^3&#xff09;。但是会超时&#xff0c;因此我们需要对暴力解法进行优化&#xff0c;而这道题我们使用双指针来进行优化&#xff0c;即依次固定一个数&#xff0c;在接下来的区间中…

vue3整合antv x6实现图编辑器快速入门

安装&#xff1a; npm install antv/x6 --save如果使用 umd 包&#xff0c;可以使用下面三个 CDN 中的任何一个&#xff0c;默认使用 X6 的最新版&#xff1a; https://unpkg.com/antv/x6/dist/index.jshttps://cdn.jsdelivr.net/npm/antv/x6/dist/index.jshttps://cdnjs.clo…

mysql树形结构返回是否叶子节点

我们界面上展示树形结构的时候往往会用到懒加载&#xff0c;做懒加载需要知道哪个节点是叶子节点&#xff0c;这样叶子节点就不需要继续往下加载了&#xff0c;这种需求可以通过sql实现 先来看下表结构 方式一,通过sql语句直接获取leaf 什么是叶子节点&#xff1f;就是没有哪…

Alternative account/备选科目代码配置说明 【1:1和国家科目配置运营科目】

业务场景&#xff1a; 有个关于mapping的问题&#xff1a; 1. 如果在GL account的master data里面&#xff0c;之前已经做好了alternative account的mapping, 那我要改成另外的local account,这样 A 会不会影响本地子公司的报表&#xff1f; 2. 如果GL account和alternativ…

Redis 缓存深度解析:穿透、击穿、雪崩与预热的全面解读

Redis 缓存深度解析&#xff1a;穿透、击穿、雪崩与预热的全面解读 一 . 什么是缓存 ?二 . 使用 Redis 作为缓存三 . 缓存的更新策略3.1 定期生成3.2 实时生成 四 . 缓存预热、缓存穿透、缓存雪崩、缓存击穿4.1 缓存预热4.2 缓存穿透4.3 缓存雪崩4.4 缓存击穿 Hello , 大家好 …

WebAPI (一)DOM树、DOM对象,操作元素样式(style className,classList)。表单元素属性。自定义属性。间歇函数定时器

文章目录 Web API基本认知一、 变量声明二、 DOM1. DOM 树2. DOM对象3. 获取DOM对象(1)、选择匹配的第一个元素(2)、选择匹配多个元素 三、 操作元素1. 操作元素内容2. 操作元素属性(1)、常用属性&#xff08;href之类的&#xff09;(2)、通过style属性操作CSS(3)、通过类名(cl…

wireshark安装及抓包新手使用教程

Wireshark是非常流行的网络封包分析软件&#xff0c;可以截取各种网络数据包&#xff0c;并显示数据包详细信息。常用于开发测试过程各种问题定位。本文主要内容包括&#xff1a; 1、Wireshark软件下载和安装以及Wireshark主界面介绍。 2、WireShark简单抓包示例。通过该例子学…

Ollama—87.4k star 的开源大模型服务框架!!

这一年来&#xff0c;AI 发展的越来越快&#xff0c;大模型使用的门槛也越来越低&#xff0c;每个人都可以在自己的本地运行大模型。今天再给大家介绍一个最厉害的开源大模型服务框架——ollama。 项目介绍 Ollama 是一个开源的大语言模型&#xff08;LLM&#xff09;服务工具…

IDM 工具下载 地图高程数据

巧用IDM工具 快捷下载ASTER GDEM v3高程数据 ASTER GDEM v3是NASA推出的30米高清DEM,覆盖了几乎全部的地球陆地。那么,在NASA官网怎么下载ASTER GDEM v3的地形高程数据呢? 首先,你需要注册一个nasa的账号 注册网址: https://urs.earthdata.nasa.gov/users/new 注册方式和国…

基于人工智能的聊天情感分析系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 情感分析是一种自然语言处理任务&#xff0c;旨在识别文本中的情感&#xff0c;如“积极”、“消极”或“中立”。在聊天应用中&#…

iPhone手机清理软件:照片清理功能全解析

在数字化生活中&#xff0c;智能手机成为我们记录生活点滴的主要工具&#xff0c;尤其是iPhone&#xff0c;以其卓越的相机功能备受用户青睐。然而&#xff0c;成千上万的照片迅速堆积&#xff0c;不仅占用了大量存储空间&#xff0c;还使得设备运行缓慢。在众多解决方案中&…

多环境jdk安装,CentOS,统信UOS,Ubuntu,KylinOS,windows

文章目录 1.CentOS1.1yum安装1.2压缩包安装 本文档只是为了留档方便以后工作运维&#xff0c;或者给同事分享文档内容比较简陋命令也不是特别全&#xff0c;不适合小白观看&#xff0c;如有不懂可以私信&#xff0c;上班期间都是在得 1.CentOS 1.1yum安装 yum install -y jav…

【pyhton】python如何实现将word等文档中的文字转换成语音

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

【TomCat】安装部署

首先得进行Java的安装和部署java1.8对应tomcat9 TomCat下载Apache Tomcat - Apache Tomcat 10 Software Downloads

Verilog和Matlab实现RGB888互转YUV444

文章目录 一、色彩空间1.1 RGB色彩空间1.2 CMYK色彩空间1.3 YUV色彩空间 二、色彩空间转换公式2.1 RGB转CMYK2.2 CMYK转RGB2.3 RGB888转YUV4442.4 YUV444转RGB888 三、MATLAB实现RGB888转YUV4443.1 matlab代码3.2 matlab结果 四、Verilog实现RGB888转YUV444 一、色彩空间 色彩空…