C++11_右值引用与移动语义

       

目录

1、左值的定义

1.1 左值引用

2、右值的定义

2.1 右值引用

3、右值与左值的使用区别 

4、右值引用的意义

4.1 左值引用的短板 

5、移动语义

5.1 移动构造

5.2 移动赋值 

6、万能引用

6.1 右值的别名-左值化

6.2 完美转发 


前言:

        在C++11之前就有了引用这个概念,只是全部都是作用于左值的,即给左值取别名。而到了C++11后,引入了右值引用的概念,即给右值取别名,无论是左值引用还是右值引用实际上都是给对象取别名,只不过定义左值与右值的概念不同。

1、左值的定义

         第一眼看到左值可能会以为出现在‘=’左边的值都称为左值,的确出现在‘=’左边的值都可以称为左值,但是左值也可以出现在‘=’的右边,示例如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;int main()
{int a = 1;int c = a;//a是一个左值,但是他可以出现在‘=’右边//c也是左值return 0;
}

        通常我们会认为可以被修改的值叫做左值,而在此之前我们会把一些不能被修改的值叫做常量(不认为其是左值),或者说其具有常属性(比如被const修饰了的左值),那么被const修饰过的左值确实不能被修改了,但是他依然是左值,原因在于凡是可以被赋值或被取地址的都叫做左值。示例代码如下: 

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;int main()
{// 以下的p、a、c、*p都是左值int* p = new int;//可以对p进行赋值或者取地址int a = 1;//可以对a进行赋值或者取地址const int c = 2;//虽然不能对c进行赋值,但是可以取其地址return 0;
}

1.1 左值引用

        左值引用顾名思义就是给左值进行取别名,示例如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;int main()
{// 以下的p、a、c、*p都是左值int* p = new int;//可以对p进行赋值或者取地址int a = 1;//可以对a进行赋值或者取地址const int c = 2;//虽然不能对c进行赋值,但是可以取其地址// 以下几个是对上面左值的左值引用(侧面可以证明上述是左值)int*& rp = p;//rp是一个指针的别名,他指向p指向的内容int& ra = a;const int& rc = c;int& rvp = *p;//rvp是一块类型为int类型空间的别名return 0;
}

2、右值的定义

        左值可以出现在‘=’号的左右,但是右值只能出现在‘=’号的右边,因此意味着右值是不允许被直接修改,比如字面常量、函数的值返回、表达式的结果都是右值,并且不能够直接对他们进行修改,也不能够直接取右值的地址

        示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;double func(double x, double y)
{return x > y ? y : x;
}int main()
{double x = 1.1, y = 2.2;// 以下几个都是常见的右值12;x + y;func(x, y);//	以下三行代码会报错,原因就是不能够直接对右值进行修改12 = 1;x + y = 1;func(x, y) = 1;//  并且不能够直接取他们的地址&12;&(x + y);&func(x, y);return 0;
}

        函数值返回和表达式结果为右值的原因如下图所示:

2.1 右值引用

        左值引用的写法一般是在类型的右边加上一个取地址符号‘&’,而右值引用的写法是在类型的右边加上两个取地址符号‘&&’,并且对右值进行引用后,可以取到别名的地址,并且可以对别名进行修改

        示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;double func(double x, double y)
{return x > y ? y : x;
}int main()
{double x = 1.1, y = 2.2;// 以下几个都是常见的右值12;x + y;func(x, y);// 以下几个都是对右值的右值引用int&& rr1 = 12;double&& rr2 = x + y;double&& rr3 = func(x, y);//可以对别名进行修改和取地址cout << ++rr1 << endl;cout << &rr1 << endl;cout << ++rr2 << endl;cout << &rr2 << endl;cout << ++rr3 << endl;cout << &rr3 << endl;return 0;
}

        运行结果:

3、右值与左值的使用区别 

        1、左值引用符号是一个取地址符‘&’,右值引用符号是两个取地址符‘&&’。

        2、左值可以对其进行赋值和取地址,右值不可以对其进行赋值和取地址。

        3、左值可以出现在‘=’号的左右,但是右值只能出现在‘=’号的右边。

        4、左值引用不能引用右值,右值引用不能引用左值。

        5、在第4点的基础上,左值引用加上const后可以引用右值,右值引用可以引用被函数move调用后的左值。

        上述第五点的测试代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;int main()
{const int& a = 1;//左值引用右值int b = 12;int&& rb = move(b);//右值可以引用被move后的左值rb = 1212;//并且可以通过rb更改b的值cout << b << endl;cout << rb << endl;return 0;
}

        运行结果:

4、右值引用的意义

         既然左值引用被const修饰后可以引用右值,那么为什么还要引出右值引用这个概念呢?因为左值引用被const修饰后虽然可以引用右值,但是编译器区分不了被引用的对象是左值还是右值,最主要的是被修饰的右值不能够被修改了。体现这一细节的代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;void func(const int& a)//期望左值进该函数
{cout << "void func(int& a)" << endl;
}void func(int&& a)//期望右值进该函数
{cout << "void func(int&& a)" << endl;
}int main()
{int a = 12;func(a);func(12);return 0;
}

        运行结果:


        若没有右值引用,则上述的代码中,两次调用func函数都只能进入void func(const int& a),那么我们所期望的调用左值和右值所呈现不同内容的目的就实现不了了。

4.1 左值引用的短板 

        左值引用常常被用于函数的形参函数的传引用返回,因为在这种场景下左值引用可以减少一些不必要的拷贝工作,可以节省空间和提供效率,但是传引用返回有局限性,即引用的对象的生命周期必须出了该函数栈帧还存在。因此当我们想返回一个局部对象给到外部时,就不能使用传引用返回了,因为涉及到权限越界访问的问题。

        所以当返回局部对象时,会经过两重的拷贝构造,具体示意图如下:

        从上图可以看到,临时对象的作用只是对st1对象进行深拷贝,并且拷贝任务完成后会销毁,如果能够把临时对象的内容充分利用到极致,就可以大大的提供效率了,而右值引用的作用在这里就体现出来了,对临时对象进行右值引用,则就可以对临时对象的内容进行修改了,通常会利用右值引用将临时对象里的数据和st1的数据进行交换,这样一来就省去了深拷贝这个步骤,并且把这一步叫做移动构造

5、移动语义

        移动语义即移动构造移动赋值,他的作用主要是通过资源的交换从而避免一些繁琐的工作,达到提高效率的目的,实现移动语义的前提是右值引用。

5.1 移动构造

        移动构造的思想是将右值中的资源进行转移,并用来初始化另一个对象,并且把未初始化对象的内容给到该右值,形象的来说就是窃取右值的资源来构造新的对象,这时候就无需进行深拷贝了。

        移动构造其实是拷贝构造的一个函数重载,只不过拷贝构造的形参类型一般是const+左值引用,因为拷贝构造不希望修改被拷贝的对象。而移动构造的形参类型是没有加const的右值引用,可以通过其别名修改右值。具体示意图如下:


        因此有了移动构造后,当右值要调用拷贝构造就会进入移动构造,而不是拷贝构造,比如上述例子:

5.2 移动赋值 

        移动赋值的思想和移动构造是一样的,也是通过资源的交换实现的。

        移动赋值与拷贝赋值在写法上的区别: 

        移动赋值的实现代码:

// 移动赋值
string& operator=(string&& s)
{swap(s);return *this;
}

        移动赋值的调用过程:

6、万能引用

         万能引用又叫引用折叠,他可以引用左值和右值,但是他的写法和右值引用是一样的,只不过他作为一个模板参数,要作用在函数模板下。

        万能引用写法如下:

template<typename T>
void Universal_citation(T&& t)
{//此处的形参T&&表示的是万能引用,t可以接收左值和右值//t接收右值时是右值引用,接收左值时被折叠成了左值引用
}

6.1 右值的别名-左值化

         从上文可以得到,右值的别名是可以更改的,比如以上的移动构造移动赋值,他们的形参都是一个右值引用,但是右值本身是不可以被更改的,当右值通过右值引用后,就可以通过别名来更改右值的内容,所以才能够在移动构造移动赋值的函数中进行资源的交换。形象的来说,右值的别名可以看作是一个左值

        比如以下代码就可以证明右值别名的左值化:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;void func(int& x) { cout << "左值引用" << endl; }
void func(const int& x) { cout << "const 左值引用" << endl; }
void func(int&& x) { cout << "右值引用" << endl; }
void func(const int&& x) { cout << "const 右值引用" << endl; }template<typename T>
void Universal_citation(T&& t)//此处的形参T&&表示的是万能引用,可以接收左值和右值
{func(t);
}
int main()
{Universal_citation(10);int a;Universal_citation(a);//左值Universal_citation(move(a)); // 右值const int b = 8;Universal_citation(b);//const 左值Universal_citation(move(b)); // const 右值return 0;
}

        运行结果:

        从结果可以发现,调用函数Universal_citation的实参是右值,但是经过右值引用后,调用func函数的实参变成了左值,因此打印出来的都是”左值引用“。

6.2 完美转发 

        完美转发的写法如下:

std::forward<T>

         他主要是用于参数在传递的过程中可以保留原来的属性,比如其可以让右值通过右值引用后,别名依然保留右值属性。注意:他的生效范围和move一样,只在当前行有效,比如:move(a),在当前行可以使a变成右值,但是到了下一行a还是左值。

        将上述代码进行完美转发: 

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;void func(int& x) { cout << "左值引用" << endl; }
void func(const int& x) { cout << "const 左值引用" << endl; }
void func(int&& x) { cout << "右值引用" << endl; }
void func(const int&& x) { cout << "const 右值引用" << endl; }template<typename T>
void Universal_citation(T&& t)//此处的形参T&&表示的是万能引用,可以接收左值和右值
{func(std::forward<T>(t));
}
int main()
{Universal_citation(10);int a;Universal_citation(a);//左值Universal_citation(move(a)); // 右值const int b = 8;Universal_citation(b);//const 左值Universal_citation(move(b)); // const 右值return 0;
}

        运行结果:

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

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

相关文章

地址分词 | EXCEL批量进行地址分词,标准化为十一级地址

一 需求 物流需要对用户输入地址进行检查&#xff0c;受用户录入习惯地址可能存在多种问题。 地址标准化是基于地址引擎和地址大数据模型&#xff0c;自动将地址信息标准化为省、市、区市县、街镇、小区、楼栋、单元、楼层、房屋、房间等元素&#xff0c;补充层级缺失数据、构建…

bug - poi getMergedRegion合并后的行列number错误

第一个CellRangeAddress 的Row number 应该是0&#xff0c;但是给出的是1。 其它的CellRangeAddress 与实际大致相差4-5不等&#xff0c;没有规律。 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>…

ChatGPT Plus 支付出现「您的银行卡被拒绝/your card has been declined」怎么办?

ChatGPT Plus 支付出现「您的银行卡被拒绝/your card has been declined」怎么办&#xff1f; 在订阅 ChatGPT Plus 或者 OpenAI API 时&#xff0c;有时候会出现已下报错 &#xff1a; Your card has been declined. 您的银行卡被拒绝 出现这种错误&#xff0c;有以下几个解…

网关数据采集解决方案-天拓四方

随着物联网技术的快速发展&#xff0c;数据采集已成为企业运营、管理和决策的重要支撑。网关作为连接不同网络的关键设备&#xff0c;其在数据采集过程中发挥着至关重要的作用。本文将详细介绍一种网关数据采集解决方案&#xff0c;旨在确保数据采集的高效性、准确性和安全性。…

在圆钢生产中 哪种直线度测量仪更具实用性?

圆钢直线度尺寸是其品质检测中重要一环&#xff0c;要说直线度测量方法&#xff0c;那肯定是有很多种&#xff0c;但要说适合产线&#xff0c;更具实用性的是哪种&#xff0c;本文就来简单的看一下。 简单来说&#xff0c;直线度测量方法有直尺法、重力法、直线法、百分表、水平…

入门C++《类与对象》————2

目录 前言&#xff1a; 1.类的6个默认成员函数 2. 构造函数 1、概念引入&#xff1a; 2、特性&#xff1a; 3.析构函数 1、概念引入&#xff1a; 2、特性&#xff1a; 4.拷贝构造函数 1、概念&#xff1a; 2、特征&#xff1a; 5.赋值运算符重载 1、运算符重载&am…

Word论文格式怎么设置 Word论文查重功能在哪里 论文格式要求及字体大小 论文查重怎么查 WPS论文查重准确吗

Word文档是由Microsoft Word处理软件创建和编辑的文档。Word文档通常用于创建各种类型的文档&#xff0c;如信函、报告、简历、论文等。本篇文章将为大家介绍Word论文格式怎么设置以及Word论文查重功能在哪里。 一、Word论文格式怎么设置 一个好的论文格式&#xff0c;是论文…

机器学习——神经网络压缩

神经网络压缩 需要部署&#xff0c;设备内存和计算能力有限&#xff0c;需要进行模型压缩&#xff0c;在设备上运行的好处是低延迟&#xff0c;隐私性。 目录 不考虑硬件问题&#xff0c;只考虑通过软件算法优化。 修剪网络 参数过多或者没有用的参数&#xff0c;可以将其剪…

Missing type map configuration or unsupported mapping

今天开发的时候突然遇到这么一个问题&#xff0c;可以确定的是不是AutoMap的问题&#xff0c;因为项目中其他接口都是好好的&#xff0c;只有新加的这个控制器不行&#xff0c;排查了一下&#xff0c;少了映射配置&#xff0c;在这里加上映射关系即可&#xff0c;大意了。

【Unity】ABB CRB 15000 外部引导运动

一、RobotStudio控制器的文件系统和配置参数 HOME&#xff1a;控制器文件系统的根目录或起始点。配置&#xff1a;机器人控制器的配置设置和参数。外件信息&#xff1a;连接到机器人的外部组件的信息。I/O 系统&#xff1a;输入/输出系统&#xff0c;管理机器人和外部设备之间的…

JavaScript 入门手册(一)

目录 一、JavaScript 是什么? 1.1 JavaScript 介绍 1.2 JavaScript 与 ECMAScript 的关系 1.3 JavaScript 是脚本语言 1.4 JavaScript 的特点 1.5 运行 JavaScript 1.6 保留关键字 二、Node.js 是什么&#xff1f; 2.1 运行时是什么&#xff1f; 2.2 Node.js 的组成…

用WSGI发布flask到centos7.9

起因 想把自己的Flask或者Django网站&#xff0c;发布到服务器上&#xff0c;让大家都可以访问。网上搜的结果&#xff0c;要么是用NginxuWSGI&#xff0c;要么是用NginxGunicorn。大名鼎鼎的Nginx我早有耳闻&#xff0c;那么两位俩玩意是啥呢。 WSGI是什么 uwsgi是Nginx和w…

存内计算生态环境搭建以及软件开发

在当今数据驱动的商业世界中&#xff0c;能够快速处理和分析大量数据的能力变得越来越重要。而存内计算开发环境在此领域发挥其关键作用。存内计算环境利用内存&#xff08;RAM&#xff09;而非传统的磁盘存储来加速数据处理&#xff0c;提供了一个高效和灵活的平台。这种环境的…

借着ChatGPT的人机交互聊聊长连接

ChatGPT这两年可谓风靡全球&#xff0c;尤其是最近Sora视频模型的横空出世以及claude 3模型所具备的浅意识&#xff0c;更是像打开了新世界的大门。本文就从ChatGPT的网页聊天开始聊起&#xff08;有蹭热度之嫌&#xff0c;哈哈&#xff09;&#xff0c;聊聊长连接的发展历程和…

React-Redux中actions

一、同步actions 1.概念 说明&#xff1a;在reducers的同步修改方法中添加action对象参数&#xff0c;在调用actionCreater的时候传递参数&#xff0c;数会被传递到action对象payload属性上。 2.reducers对象 说明&#xff1a;声明函数同时接受参数 const counterStorecre…

指针进阶(4)看一下这些与指针有关的题你都会做吗?

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

【E3S独立出版】2024年第二届绿色建筑国际会议(ICoGB 2024)

会议简介 Brief Introduction 2024年第二届绿色建筑国际会议(ICoGB 2024) 会议时间&#xff1a;2024年5月22日-24日 召开地点&#xff1a;意大利米兰 大会官网&#xff1a;www.icogb.org ICoGB 2024由意大利米兰理工大学主办&#xff0c;西安交通大学&#xff0c;葡萄牙米尼奥大…

什么是Java内存模型

当问到 Java 内存模型的时候&#xff0c;一定要注意&#xff0c;Java 内存模型&#xff08;Java Memory Model&#xff0c;JMM&#xff09;它和 JVM 内存布局&#xff08;JVM 运行时数据区域&#xff09;是不一样的&#xff0c;它们是两个完全不同的概念。 1.为什么要有 Java …

【校园安全】支小蜜防校园霸凌语音识别系统的好处

在校园安全领域&#xff0c;防校园霸凌语音识别系统的出现&#xff0c;为预防和应对校园霸凌行为提供了新的技术手段。本文将探讨防校园霸凌语音识别系统的好处&#xff0c;并分析其在校园安全建设中的重要作用。 通过安装在校园各个角落的语音识别设备&#xff0c;系统能够捕…

蓝桥杯倒计时 36天-DFS练习2

文章目录 黄金二叉树混沌之力2 黄金二叉树 思路一&#xff1a;递推做法 #include<bits/stdc.h> using namespace std;const int N 1e510;int A[N]; int B[N]; int n,sum;int main( ){cin>>n;for(int i1;i<n;i)cin>>A[i];int left,right;for(int i1;i<…