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

news2024/11/19 6:37:21

       

目录

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.coloradmin.cn/o/1500869.html

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

相关文章

浏览器一键重新发起请求

一、需求场景 在前端开发过程中&#xff0c;经常会需要重新请求后台进行代码调试&#xff0c;之前的常规方法是刷新浏览器页面或者点击页面进行交互&#xff0c;这样对多个请求的场景就很方便&#xff0c;但是往往很多时候我们只是单纯的想重新发起一个请求&#xff08;多个请求…

c++ 11 新特性 不同数据类型之间转换函数之const_cast

一.不同数据类型之间转换函数const_cast介绍 const_cast是C11中引入的一种类型转换操作符&#xff0c;用于修改类型的const或volatile属性。const_cast的主要用途是移除对象的常量性&#xff0c;它是唯一具有此能力的C风格的转型操作符。在C11中&#xff0c;const_cast可以完成…

YouTube Premium 会员氪金教程

前言 很多小伙伴喜欢看看YouTube但是有些需要会员才可以查看&#xff0c;会员可以免广告&#xff0c;根据个人需求选择是否需要开通 1、点击个人账号的头像&#xff0c;选择“购买内容和会员” 2、选择个人&#xff0c;点击免费试订&#xff0c;第一个月是免费&#xff0c;如…

智能驾驶规划控制理论学习08-自动驾驶控制模块(轨迹跟踪)

目录 一、基于几何的轨迹跟踪方法 1、基本思想 2、纯追踪 3、Stanly Method 二、PID控制器 三、LQR&#xff08;Linear Quadratic Regulator&#xff09; 1、基本思想 2、LQR解法 3、案例学习 基于LQR的路径跟踪 基于LQR的速度跟踪 4、MPC&#xff08;Mode…

【万题详解】DFS搜索专题合集(上)

专栏推荐 我的专栏——专栏链接 1.文章平均质量分 70分以上 2.以洛谷题为基础&#xff0c;解决C问题 3.有题目、讲解、思路、参考代码…… 4. 文章数&#xff1a;29 &#xff08;2024.3.8&#xff09; 课前C小程序&#xff08;脱控极域电子教室&#xff09; 这个图标相信…

‘老生常谈’ - 图书管理系统

在软件开发中&#xff0c;增、删、改、查这几个业务非常常见 增 基于axios从服务器拿到需要数据&#xff0c;进行渲染、封装&#xff0c;新增数据并不是一条一条渲染&#xff0c;而是整体重新渲染&#xff1b; 对于该系统新增数据&#xff1a;收集表单数据、提交服务器保存 -…

C++ 实战项目之 Boost 搜索引擎

项目地址&#xff1a;https://gitee.com/Vertas/boost-searcher-project 1. 项目背景 日常生活中我们使用过很多搜索引擎&#xff0c;比如百度&#xff0c;搜狗&#xff0c;360搜索等。我们今天是要实现一个像百度这样的搜索引擎嘛&#xff1f;那是不可能的&#xff0c;因为像…

听 GPT 讲 client-go 源代码 (24)

分享更多精彩内容&#xff0c;欢迎关注&#xff01; File: client-go/applyconfigurations/batch/v1/jobstatus.go 在client-go的applyconfigurations/batch/v1/jobstatus.go文件中&#xff0c;定义了与Job的状态相关的配置和操作。 文件中定义了以下几个结构体&#xff1a; Jo…

投标中项目组织结构的设置以及调整(样式表,多级列表)

投标中项目组织结构的设置以及调整&#xff08;样式表&#xff0c;多级列表&#xff09;&#xff1a; 投标项目中需要处理大规模的文字排版&#xff0c;就是需要用到样式表&#xff08;解决层级关系&#xff09;&#xff0c;多级列表&#xff08;解决自动编号的问题&#xff0…

WatiN——Web自动化测试(三)【弹出窗口处理】

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

Day25:安全开发-PHP应用文件管理模块包含上传遍历写入删除下载安全

目录 PHP文件操作安全 文件包含 文件删除 文件编辑 文件下载 云产品OSS存储对象去存储文件(泄漏安全) 思维导图 PHP知识点 功能&#xff1a;新闻列表&#xff0c;会员中心&#xff0c;资源下载&#xff0c;留言版&#xff0c;后台模块&#xff0c;模版引用&#xff0c;框…

7个实用的CSS技巧

1. First letter drop 首字母丢失 我们可以使用 :first-letter 来删除文本的第一个字母&#xff1a; p:first-letter {font-size: 200%;color: #8A2BE2; }:first-letter 选择器用于指定元素的首字母样式&#xff0c;它仅适用于块级元素。效果如下: codepen.io/OMGZui/pen/… …

Qt 5.14.2 网络编程揭秘:构建高效HTTP客户端与文件下载器

引言 在当今的软件开发世界中&#xff0c;网络通信已成为不可或缺的一部分。Qt&#xff0c;作为一个跨平台的C框架&#xff0c;为我们提供了强大的网络编程能力。本文将带你深入Qt的网络模块&#xff0c;探索如何使用QNetworkAccessManager、QNetworkRequest和QNetworkReply等核…

读《文明之光》第1册总结

人类几千年的文明史和地球的历史相比&#xff0c;实在是太短暂了&#xff0c;大约相当于几分钟和一年的关系。人类已经走过的路&#xff0c;相比今后要走的漫漫长路&#xff0c;只能算是刚刚起步。如果跳出一个个具体事件&#xff0c;站在历史的高度去看&#xff0c;我们会发现…

找出单身狗1,2

目录 1. 单身狗12. 单身狗2 1. 单身狗1 题目如下&#xff1a; 思路&#xff1a;一部分人可能会使用对数组排序&#xff0c;遍历数组的方式去找出只出现一次的数字&#xff0c;但这种方法的时间复杂度过高&#xff0c;有时候可能会不满足要求。 有一种十分简便的方法是使用异或…

类与对象(三)--static成员、友元

文章目录 1.static成员1.1概念&#x1f3a7;面试题✒️1.2static的特性&#x1f3a7;1.3思考&#x1f3a7; 2.友元2.1什么是友元&#xff1f;&#x1f3a7;2.2两种友元关系&#xff1a;&#x1f3a7; 1.static成员 1.1概念&#x1f3a7; &#x1f50e; static关键字用于声明类…

Python教程,python从入门到精通 第1天 温习笔记

1.1 字面量 1.2 注释 1.3 变量 1.4 数据类型 1.5 数据类型转换 1.6 标识符 1.7 运算符 1.8 字符串的三种定义方式 1.9 字符串拼接 1.10 字符串格式化 1.11 掌握格式化字符串的过程中做数字的精度控制 1.12 掌握快速字符串格式化的方式 1.13 字符串格式化&#xff0d;表达式的格…

动态规划基础模型总结

前言 动态规划是用一个数来表示一堆状态&#xff0c;可以看成是对暴搜的优化 暴搜一个一个数枚举过去&#xff0c;但DP是一堆堆数枚举&#xff0c;效率会快很多&#xff1b; 状态数用几维表示&#xff1a;从小到大考虑&#xff0c;看怎么样才能够让答案清楚表达出来 需要一定的…

下载一些ROS的包的方式

ROS Index 我们可以去ROS Index网站下载一些我们需要的包。打开浏览器在网址框输入index.ros.org。或者点击此处链接ROS Index 在这个网站中我们可以浏览并找到我们需要的包&#xff0c;也可以下载它的源代码或者仅安装到我们的系统中来使用。&#xff08;安装过程在终端中进行…

LeetCode的使用方法

LeetCode的使用方法 一、LeetCode是什么&#xff1f;1.LeetCode简介2.LeetCode官网 二、LeetCode的使用方法1.注册账号2.力扣社区力扣编辑器 2.1 讨论发起讨论参与讨论关注讨论 2.2 文章撰写文章关注文章 3.力扣面试官版测评面试招聘竞赛 4.力扣学习LeetBook 书架我的阅读猜您喜…