c++11特性:右值引用的作用以及使用

news2024/9/21 14:04:05

右值:

C++11 增加了一个新的类型,称为右值引用( R-value reference),标记为 &&。在介绍右值引用类型之前先要了解什么是左值和右值:

1. lvalue 是locator value的缩写,rvalue 是 read value的缩写

2. 左值是指存储在内存中、有明确存储地址(可取地址)的数据;

3. 右值是指可以提供数据值的数据(不可取地址);

通过描述可以看出,区分左值与右值的便捷方法是:可以对表达式取地址(&)就是左值,否则为右值 。所有有名字的变量或对象都是左值,而右值是匿名的。

下面的一段代码讲述了左值引用和右值引用的初始化方式: 

#include<iostream>
using namespace std;

int main()
{
	// 左值
	int num = 9;
	// 左值引用
	int& a = num;
	// 右值引用
	int&& b = 8;
	//常量右值引用
	const int&& d = 6;
	// 常量左值引用
	const int& c = num;
	const int& f = b;
	const int& g = d;
	const int& h = a;
	// 由此可以看出常量左值引用是万能的引用类型
	// 可以用同类型的各种引用来初始化的左值引用
#if 0
	const int&& e = b;// error
	int&& f = b;// error
#endif
	return 0;
}

右值引用:

右值引用就是对一个右值进行引用的类型。因为右值是匿名的,所以我们只能通过引用的方式找到它。无论声明左值引用还是右值引用都必须立即进行初始化,因为引用类型本身并不拥有所绑定对象的内存,只是该对象的一个别名。通过右值引用的声明,该右值又“重获新生”,其生命周期与右值引用类型变量的生命周期一样,只要该变量还活着,该右值临时量将会一直存活下去。

举一个例子:

A a = 临时;

要基于这个临时对象给a对象初始化,假设这个a对象是非常庞大的。将这个临时对象构建出来需要时间,将数据拷贝给a对象也需要时间,然后就被析构了。也就是说这个临时对象从创建到被销毁存活的时间是非常短的,虽然存活时间短,但是耗费了大量的系统资源。这时就有一种方法让这个临时对象不销毁,直接使用他。这个时候就需要使用右值引用了,延长存活周期。这时候这个a对象就不是拷贝临时对象了,而是引用了这个临时对象。

 关于右值引用的使用,参考代码如下:

#include<iostream>
using namespace std;

class Test
{
public:
	Test() : m_num(new int(100))
	{
		cout << "construct:my name is jerry" << '\n';
		printf("m_num 地址:%p\n", m_num);
	}

	// 拷贝构造
	Test(const Test& a) : m_num(new int(*a.m_num))
	{
		cout << "copy construct:my name is tom" << '\n';
	}

	~Test()
	{
		cout << "destruct Test class ... " << '\n';
		delete m_num;
	}

	int* m_num;
};

Test getObj()
{
	Test t;
	return t;
}


int main()
{
	// t对象会被getObj返回的对象实例化,但是函数中的对象t就会自动析构
	// 等主函数中的t对象生命周期结束的时候,t对象也会自动析构
	Test t = getObj();// 拷贝构造




	return 0;
}

上述代码的运行结果为:

construct: my name is jerry
m_num 地址:0x7ffca2c02790
copy construct: my name is tom
destruct Test class...
destruct Test class...

输出结果与上述代码分析的一样,这就验证了我们的分析是正确的。

但是现在的编译器可能会进一步优化,使输出结果变为:

construct:my name is jerry
m_num 地址:000000ABB6DDFA28
destruct Test class ...

 优化的部分:getObj()调用Test t的默认构造函数,return t隐式调用复制构造函数创建一个临时对象(此步骤被编译器优化了),main中Test t = getObj()又调用了复制构造函数

 使用一个右值引用的构造函数来优化,这个右值引用的构造函数也称为移动构造函数

 下面是添加移动构造函数的示例:

#include<iostream>
using namespace std;

class Test
{
public:
	Test() : m_num(new int(100))
	{
		cout << "construct:my name is jerry" << endl;
		printf("m_num 地址:%p\n", &m_num);
	}

	// 拷贝构造
	Test(const Test& a) : m_num(new int(*a.m_num))
	{
		cout << "copy construct:my name is tom" << endl;
	}

	// 移动构造函数的作用:复用其他对象中的资源(堆内存)
	// 因为这个堆内存已经在另一个对象中被申请出来了,并且已经被初始化了
	// 所以就没有必要在新的对象中再去申请新的资源了,并且还要对这个新对象做相同的初始化
	Test(Test&& a) : m_num(a.m_num)// 让当前对象的指针指向a对象的m_num指针
	{
		// 所以通过移动构造做的是一个浅拷贝
		// 不能让a对象析构的时候将这个块内存析构掉了
		// 让指针指向空就好了
		// 这样当前对象就可以继续使用a对象中的m_num这个指针了
		a.m_num = nullptr;
		cout << "move construct ... " << endl;
	}

	~Test()
	{
		cout << "destruct Test class ... " << endl;
		delete m_num;
	}

	int* m_num;
};

Test getObj()
{
	Test t;
	return t;
}

int main()
{
	// t对象会被getObj返回的对象实例化,但是函数中的对象t就会自动析构
	// 等主函数中的t对象生命周期结束的时候,t对象也会自动析构
	Test t = getObj();// 拷贝构造
	// getObj()调用Test t的默认构造函数,
	// return t隐式调用复制构造函数创建一个临时对象(此步骤被编译器优化了),
	// main中Test t = getObj()又调用了复制构造函数
	return 0;
}

 上述代码的运行结果为:

construct:my name is jerry
m_num 地址:0x7ffcb9c02790
move construct ...
destruct Test class ... 
destruct Test class ... 

注意: 这个移动构造函数调用的并不是getObj()对象t中的所有的资源,而是某一部分资源(堆内存资源)。这样就没有必要拷贝了。

接下来来思考:为什么添加了移动构造后,拷贝构造就不调用了呢?

在进行赋值操作的时候,编译器就会判断,右边的这个是不是临时对象。如果是临时对象就会优先调用移动构造。若不是临时对象,那么调用的还是拷贝构造函数。

 临时对象也可以用右值引用来接收:

int main()
{
	// t对象会被getObj返回的对象实例化,但是函数中的对象t就会自动析构
	// 等主函数中的t对象生命周期结束的时候,t对象也会自动析构
	Test t = getObj();// 拷贝构造
	// getObj()调用Test t的默认构造函数,
	// return t隐式调用复制构造函数创建一个临时对象(此步骤被编译器优化了),
	// main中Test t = getObj()又调用了复制构造函数
	cout << endl;
	Test&& t1 = getObj();
	printf("m_num 地址:%p\n", &t1.m_num);
	return 0;
}

输出结果为:

construct:my name is jerry
m_num 地址:000000C0172FF648
copy construct:my name is tom
destruct Test class ...
destruct Test class ...

construct:my name is jerry
m_num 地址:000000C0172FF688
move construct ... 
m_num 地址:000000C0172FF688
destruct Test class ...

 由上述输出结果可以看出,移动构造就是用的同一个地址。

 使用右值引用续命:

#include<iostream>
using namespace std;

class Test
{
public:
	Test() : m_num(new int(100))
	{
		cout << "construct:my name is jerry" << endl;
		printf("m_num 地址:%p\n", &m_num);
	}

	// 拷贝构造
	Test(const Test& a) : m_num(new int(*a.m_num))
	{
		cout << "copy construct:my name is tom" << endl;
	}


	~Test()
	{
		cout << "destruct Test class ... " << endl;
		delete m_num;
	}

	int* m_num;
};

Test getObj()
{
	Test t;
	return t;
}

Test getObj1()
{
	return Test();// 返回临时的匿名对象
}

int main()
{
	// t对象会被getObj返回的对象实例化,但是函数中的对象t就会自动析构
	// 等主函数中的t对象生命周期结束的时候,t对象也会自动析构
	Test t = getObj();// 拷贝构造
	// getObj()调用Test t的默认构造函数,
	// return t隐式调用复制构造函数创建一个临时对象(此步骤被编译器优化了),
	// main中Test t = getObj()又调用了复制构造函数
	cout << endl;
	Test&& t1 = getObj();
	printf("m_num 地址:%p\n", &t1.m_num);

	// 如果没有移动构造函数,使用右值引用初始化的要求要更高一些
	// 要求右侧是一个临时的不能取地址的对象
	cout << endl;
	Test&& t2 = getObj1();
	printf("m_num 地址:%p\n", &t2.m_num);

	return 0;
}

输出结果为:

  

 使用右值引用t2给这个匿名对象续命,因为输出的地址一致。我们并没有创建t2,而是使用了即将释放的这个对象里面的所有的资源。

注意:移动构造函数中是复用了即将释放的对象里面的部分资源(堆内存),而在没有移动构造函数,使用右值引用续命是复用了即将释放的对象里面全部资源。

getObj函数也可以这样写,这个返回的就是右值引用类型 

Test&& getObj2()
{
	return Test();
}

 通过这些方式得到的对象都称为将亡值。将亡值就是即将被释放的对象。

C++11 中右值可以分为两种:一个是将亡值( xvalue, expiring value),另一个则是纯右值( prvalue, PureRvalue):

1. 纯右值:非引用返回的临时变量、运算表达式产生的临时变量、原始字面量和 lambda 表达式等。
2. 将亡值:与右值引用相关的表达式,比如,T&&类型函数的返回值、 std::move 的返回值等。

综上,使用移动构造,返回即将释放的对象,或者返回右值引用的对象都称之为将亡值。

 

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

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

相关文章

Swagger2接口测试文档

目录 一、Swagger简介 1.1 Swagger是什么&#xff1f; 1.2 为什么要用Swagger 1.3 Swagger注解 二、Spring集成Swagger 三、测试环境配置 一、Swagger简介 1.1 Swagger是什么&#xff1f; Swagger是一款RESTFUL接口的文档在线自动生成功能测试功能软件。Swagger是一个规…

(自适应手机版)英文外贸网站模板 - 带三级子目录

(自适应手机版)英文外贸网站模板 - 带三级子目录 PbootCMS内核开发的网站模板&#xff0c;该模板适用于外贸网站、英文网站类等企业&#xff0c;当然其他行业也可以做&#xff0c;只需要把文字图片换成其他行业的即可&#xff1b; 自适应手机版&#xff0c;同一个后台&#x…

数据安全扫描仪荣膺网络安全优秀创新成果大赛优胜奖 - 凸显多重优势

近日&#xff0c;由中国网络安全产业联盟&#xff08;CCIA&#xff09;主办、CCI数据安全工作委员会中国电子技术标准化研究院等单位承办的“2023年网络安全优秀创新成果大赛”获奖名单公布。天空卫士数据安全扫描仪&#xff08;DSS&#xff09;产品获得创新成果大赛优胜奖。 本…

从源码到实践:深入了解鸿鹄电子招投标系统与电子招投标

在数字化采购领域&#xff0c;企业需要一个高效、透明和规范的管理系统。通过采用Spring Cloud、Spring Boot2、Mybatis等先进技术&#xff0c;我们打造了全过程数字化采购管理平台。该平台具备内外协同的能力&#xff0c;通过待办消息、招标公告、中标公告和信息发布等功能模块…

JetBrains AI Assistant 最佳平替方案来了

先看看官方推荐 JetBrains IDE 中的 AI 助手 除了你自己&#xff0c;谁最了解你的项目&#xff1f;你的IDE&#xff01;这就是为什么 AI Assistant 可以如此具有上下文感知能力和帮助性的原因。 JetBrains AI 服务采用不同的大型语言模型 &#xff08;LLM&#xff09;&#xf…

ChatGPT助力Excel数据分析:让你的工作事半功倍!

文章目录 一、ChatGPT简介二、ChatGPT在Excel数据分析中的应用1. 数据清洗2. 数据处理3. 数据分析4. 数据可视化 三、如何使用ChatGPT进行Excel数据分析1. 安装ChatGPT插件2. 输入问题或命令3. 查看结果并调整参数4. 导出结果并分享四、总结与展望 《巧用ChatGPT高效搞定Excel数…

想将电脑屏幕共享到iPhone上,但电脑是Linux系统,可行吗?

常见Windows系统或macOS系统的电脑投屏到手机&#xff0c;难道Linux系统的电脑要投屏就是个难题吗&#xff1f; 想要将Linux系统投屏到iPhone、iPad、安卓设备、鸿蒙设备&#xff0c;其实你可以利用软件AirDroid Cast和Chrome浏览器&#xff01;连接同一网络就可以直接投屏。 第…

CSS自适应分辨率 amfe-flexible 和 postcss-pxtorem:大屏高宽自适应问题

前言 继上篇《CSS自适应分辨率 amfe-flexible 和 postcss-pxtorem》。 发现一个有趣的问题&#xff0c;文件 rem.js 中按照宽度设置自适应&#xff0c;适用于大多数页面&#xff0c;但当遇到大屏就不那么合适了。 问题 使用宽度&#xff0c;注意代码第2 和 4 行&#xff1a;…

Linux笔记---系统信息

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux学习 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 命令 1. uname - 显示系统信息 2. hostname - 显示或设置系统主机名 3. top - 显示系统资源使用情况 4. df - 显示磁盘空间使用情…

go语言函数二、init函数定义与作用

go语言init函数定义与作用 在go语言中&#xff0c;每一个源文件都可以包含一个init函数&#xff0c;这个函数会在main函数执行前&#xff0c;被go运行框架调用&#xff0c;注意是在main函数执行前。 package main import ("fmt" )func init() {fmt.Println("i…

【C++高阶(八)】单例模式特殊类的设计

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 单例模式 1. 前言2. 设计一个不能被拷贝/继承的…

顶级加密混淆混淆工具测评:ipagurd

摘要 JavaScript代码安全需求日益增长&#xff0c;因此JavaScript混淆工具的使用变得广泛。本文将对专业、商业JavaScript混淆工具ipagurd进行全面评估&#xff0c;通过比较其功能、操作便捷性、免费试用、混淆效果等方面&#xff0c;帮助开发者选择适合自己项目需求的工具。 …

stm32学习总结:4、Proteus8+STM32CubeMX+MDK仿真串口收发

stm32学习总结&#xff1a;4、Proteus8STM32CubeMXMDK仿真串口收发 文章目录 stm32学习总结&#xff1a;4、Proteus8STM32CubeMXMDK仿真串口收发一、前言二、资料收集三、STM32CubeMX配置串口1、配置开启USART12、设置usart中断优先级3、配置外设独立生成.c和.h 四、MDK串口收发…

在windows上如何干净的卸载一个软件及其快捷方式

可以在控制面板里面卸载&#xff0c;可以卸载掉文件夹及其快捷方式&#xff0c;具体操作如下&#xff1a; 找到-》控制面板\程序\程序和功能 然后右键某一项&#xff0c;即可出现卸载功能项。 卸载不干净的方法&#xff1a;利用软件商店卸载&#xff0c;有可能卸载失败&#x…

Leetcode—238.除自身以外数组的乘积【中等】

2023每日刷题&#xff08;六十六&#xff09; Leetcode—238.除自身以外数组的乘积 前缀积后缀积实现代码 class Solution { public:vector<int> productExceptSelf(vector<int>& nums) {int n nums.size();vector<int> ans(n);int pre 1, suf 1;fo…

Shell 脚本应用(二)

实验案例&#xff1a;使用Shell脚本监控主机 实验环境 某公司随着业务的不断发展&#xff0c;所使用的Linux服务器也越来越多&#xff0c;管理员希望编写一个简单的性 能监控脚本&#xff0c;放到各服务器中&#xff0c;当监控指标出现异常时发送告警邮件。 需求描述 >编…

【技术】MySQL 日期时间操作

MySQL 日期时间操作 MySQL 系统时间MySQL 时间格式化MySQL 年月日时分秒周MySQL 日期计算时分秒时差日期差日期加减 MySQL 系统时间 now()&#xff1a;系统时间&#xff0c;年月日时分秒current_date&#xff1a;系统时间&#xff0c;年月日current_time&#xff1a;系统时间&…

标准库中的string类(上)——“C++”

各位CSDN的uu们好呀&#xff0c;好久没有更新小雅兰的C专栏的知识啦&#xff0c;接下来一段时间&#xff0c;小雅兰就又会开始更新C这方面的知识点啦&#xff0c;以及期末复习的一些知识点&#xff0c;下面&#xff0c;让我们进入西嘎嘎string的世界吧&#xff01;&#xff01;…

硬件基础-电感

电感 目录 1.原理 2.作用 3.高频等效模型 4. 直流偏置特性 5.器件选型 6.电感损耗 7.功率电感 8.贴片电感 9.共模电感 10.差模电感 1.原理 电感是阻碍电流的变化,储能 电感的磁芯决定了电感的饱和电流&#xff0c;也决定了电感值与电流的变化曲线&#xff0c;磁滞损…

Leetcode—77.组合【中等】

2023每日刷题&#xff08;六十五&#xff09; Leetcode—77.组合 算法思想 实现代码 class Solution { public:vector<vector<int>> combine(int n, int k) {vector<vector<int>> ans;vector<int> path;function<void(int)> dfs [&…