【C++进阶学习】第十一弹——C++11(上)——右值引用和移动语义

news2025/1/14 0:51:49

前言:

前面我们已经将C++的重点语法讲的大差不差了,但是在C++11版本之后,又出来了很多新的语法,其中有一些作用还是非常大的,今天我们就先来学习其中一个很重要的点——右值引用以及它所扩展的移动定义

目录

一、左值引用和右值引用

左值引用 

右值引用

二、左值引用与右值引用的比较

三、右值引用的使用

移动构造

移动赋值

四、总结


一、左值引用和右值引用

左值引用 

左值引用是最常见的引用类型,通常用于绑定到一个左值。左值是一个具有名称的对象,可以取地址,通常出现在赋值操作符的左边。(简单的说,能取地址的就是左值)

语法:

类型 &引用名 = 左值;

示例:

int a = 10;
int &refA = a;  // refA是一个左值引用,绑定到左值a

特点:

  • 左值引用必须初始化,并且只能绑定到左值。
  • 左值引用可以修改绑定的对象。

右值引用

右值引用是C++11引入的新特性,用于绑定到一个右值。右值是一个临时对象,通常没有名称,不能取地址,通常出现在赋值操作符的右边。(右值不能取地址,比如常量)

语法:

类型 &&引用名 = 右值;

示例:

int &&refB = 20;  // refB是一个右值引用,绑定到右值20

特点:

  • 右值引用必须初始化,并且只能绑定到右值。
  • 右值引用主要用于实现移动语义和完美转发。

有一个需要强调的是,常变量虽然也属于常量,但是它可以取地址,所以它属于左值

二、左值引用与右值引用的比较

左值引用:

1. 左值引用只能引用左值,不能引用右值。
2. 但是const左值引用既可引用左值,也可引用右值
int main()
{
    // 左值引用只能引用左值,不能引用右值。
    int a = 10;
    int& ra1 = a;   // ra为a的别名
    //int& ra2 = 10;   // 编译失败,因为10是右值
    // const左值引用既可引用左值,也可引用右值。
    const int& ra3 = 10;
    const int& ra4 = a;
    return 0;
}

右值引用:

1. 右值引用只能右值,不能引用左值。
2. 但是右值引用可以move以后的左值。
int main()
{
 // 右值引用只能右值,不能引用左值。
 int&& r1 = 10;
 
 // error C2440: “初始化”: 无法从“int”转换为“int &&”
 // message : 无法将左值绑定到右值引用
 int a = 10;
 int&& r2 = a;
 // 右值引用可以引用move以后的左值
 int&& r3 = std::move(a);
 return 0;
}

三、右值引用的使用

在上面我们也已经讲到了,左值引用及可以引用左值,又可以引用右值,那么C++11为什么还要设计右值引用呢?下面我们来看一下原因。

我们借助string类来讲解

先来看一下下面所出现的所有代码,可以先思考看看思考思考

namespace zda
{
	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)
		{
			::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
	};
	string to_string(int value)
	{
		bool flag = true;
		if (value < 0)
		{
			flag = false;
			value = 0 - value;
		}
		string str;
		while (value > 0)
		{
			int x = value % 10;
			value /= 10;
			str += ('0' + x);
		}
		if (flag == false)
		{
			str += '-';
		}
		std::reverse(str.begin(), str.end());
		return str;
	}
}

左值引用使用场景:

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

左值引用短板:

当函数返回对象为临时变量的时候,左值引用就派不上用场了,就只能传值返回,就需要拷贝至少一次(老一点的编译器为两次)

右值引用和移动语义:
对于上面这种问题,我们就可以通过右值引用和移动语义来实现

移动构造

移动构造的本质就是将参数的右值窃取过来,占为己有,这样它就不用再深度拷贝了,所以叫做移动构造
// 移动构造
string(string&& s)
 :_str(nullptr)
 ,_size(0)
 ,_capacity(0)
{
 cout << "string(string&& s) -- 移动语义" << endl;
 swap(s);
}
int main()
{
 zda::string ret2 = bit::to_string(-1234);
 return 0;
}

当返回值是右值时,因为移动构造并没有开辟空间进行深拷贝,所以效率就会更高

需要注意的是,当拷贝构造和移动构造同时存在时,编译器默认的也会调用移动构造,因为编译器会默认调用效率更高的函数

移动赋值

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

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

这里运行后发现,调用了一次移动构造和一次移动赋值,因为这里的ret1是一个已经存在的对象,用它来接受函数返回值的时候编译器就无法再优化了,所以会在移动构造后创建一个临时变量,且这个临时变量会被编译器识别为右值,从而调用移动赋值

四、总结

上面我们就简单的先提了一下右值引用的应用:移动语义,下一篇我们再重点讲解一下右值引用的另一个重点语法:完美挥发

感谢各位大佬观看,创作不易,还请各位大佬点赞支持一下!!!

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

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

相关文章

AI智驾时代降临,端到端奏响“三重奏”

“追上未来&#xff0c;抓住它的本质&#xff0c;把未来转变为现在”&#xff0c;俄国哲学家车尔尼雪夫斯曾这样描述未来。而走到今天的新能源汽车&#xff0c;其通向未来的本质就是做好智能化。 呐喊智能化的口号&#xff0c;从2023年延续到2024年。如今&#xff0c;智能化的…

如何清理电脑浏览器缓存和内存 macbookpro浏览器怎么清理

浏览器已经成为我们日常生活中不可或缺的工具。然而&#xff0c;随着时间的推移&#xff0c;浏览器缓存的积累可能会逐渐影响我们的上网体验&#xff0c;导致网页加载速度变慢、浏览器运行卡顿等问题。因此&#xff0c;定期清理浏览器缓存变得尤为重要。那么Mac怎么清除浏览器缓…

单元测试之打桩-stub

首先&#xff0c;了解两个单元测试和集成测试的基本概念&#xff1a; 驱动程序/驱动模块&#xff08;driver&#xff09;&#xff0c;用以模拟被测模块的上级模块。驱动模块在集成测试中接受测试数据&#xff0c;把相关的数据传送给被测模块&#xff0c;启动被测模块。 桩程序…

Kubernetes Prometheus 系列 | AlertManager实现企业微信报警

helm部署prometheusgrafana直通车&#xff08;与本文章关联&#xff09; 首先注册企业微信&#xff1a;https://work.weixin.qq.com/ 目录 一、第一种根据企业id&#xff0c;应用secret等绑定二、第二种方式-添加群聊天机器人webhook&#xff08;推荐&#xff09; 前言&#x…

滑动窗口专题

前言&#xff1a; ①单调队列模板&#xff08;左边是队头hh&#xff0c;右边是队尾tt&#xff09; 首先维护两个指针hh和tt的位置 常见模型&#xff1a;找出滑动窗口中的最大值/最小值 int hh 0, tt -1; for (int i 0; i < n; i ) {while (hh < tt && check_…

区间DP---多边形 与金字塔

多边形&#xff1a;https://www.acwing.com/problem/content/285/ 其实就是环形的石子合并&#xff0c;只不过由于乘法的存在还要记录一下最大值与最小值。 AC代码&#xff1a; #include <bits/stdc.h> using namespace std; int a[105]; char b[105]; int dpmax[105]…

U盘跨机使用难题:打不开的困境与数据恢复之道

在数字时代&#xff0c;U盘作为我们日常数据交换和存储的重要工具&#xff0c;扮演着不可或缺的角色。然而&#xff0c;当您满怀期待地将U盘插入另一台电脑&#xff0c;却遭遇“无法打开”的尴尬时&#xff0c;那份焦急与无助可想而知。本文将深入探讨“U盘在别的电脑用了后打不…

隐写工具steghide linux编译安装

1、git clone https://github.com/StefanoDeVuono/steghide.git 2、autoreconf -i 3、./configure 4、make 编译完成后再src目录下又steghide执行下程序 报错&#xff1a;configure: error: cannot find required auxiliary files: compile时需要执行autoreconf 如果往j…

外卖项目day11---微信支付(跳过微信接口进行伪支付,超超超细节哦!!!)/cpolar内网穿透工具使用

这里一般个人是没有的&#xff0c;我们用不到 内网穿透工具临时获取到临时域名(可用可不用&#xff0c;拓展一下也好&#xff09; 安装客户端&#xff0c;在相应目录打开cmd窗口&#xff0c;输入以下代码 可以看到这里的请求地址变了&#xff0c;这就是内网穿透工具的作用 微信…

Windows Server 2025 Preview 部署 Ⅱ—— 在ESXi 8上安装 Windows Server 2025

目录 3. 在ESXi 8上安装 Windows Server 20253.1 创建Win Server 虚拟机&#xff08;1&#xff09;创建 VM&#xff08;2&#xff09;选择创建类型&#xff08;3&#xff09;设置VM名和选择guest OS&#xff08;4&#xff09;选择存储&#xff08;5&#xff09;自定义虚拟机组件…

leafletjs-gis中如何判断图层或元素是点,线,面的类型

查找地图上的图层&#xff0c;要素&#xff1a; window.MapLeaflet.eachLayer(function (layer) {if (layer ! window.MapLeaflet) {console.log("layer instanceof L.Polyline", layer instanceof L.Polyline)// L.Marker,L.Polyline, L.Polygon||L.Rectangle,L.C…

spring boot(学习笔记第十六课)

spring boot(学习笔记第十六课) Spring boot的websocket(点对点) 学习内容&#xff1a; Spring boot的websocket&#xff08;点对点&#xff09;spring boot(学习笔记第十六课) 1. Spring boot的websocket&#xff08;点对点&#xff09; 前面练习了websocket的广播模式。 接…

1.GPIO

理论说明 输入 上拉输入&#xff1a;拉高电平 下拉输入&#xff1a;拉低电平 浮空输入&#xff1a;不拉高也不拉低电平 输出 开漏输出&#xff1a;不能输出高电平&#xff08;P-MOS不可用&#xff0c;则只能低电平&#xff09; 推挽输出&#xff1a;可输出高低电平 输出速率…

informer中DeltaFIFO机制的实现分析与源码解读

informer中的DeltaFIFO机制的实现分析与源码解读 DeltaFIFO作为informer中重要组件&#xff0c;本文从源码层面了解是如何DelatFIFO是实现的。 DeltaFIFO的定义 找到delta_fifo.go的源码&#xff0c;位于client-go/tools/cache/delta_fifo.go 代码结构大致如下: store定义…

【priority_queue的模拟实现】

priority_queue的模拟实现 小杨 容器适配器&#xff1a;priority_queue priority_queue即优先级队列是一种容器适配器&#xff0c;根据严格的弱排序标准(即排序规则可以更改),他的第一个元素总是它所包含的元素中最大的。优先队列将特定容器类封装作为其底层容器类。标准容器类…

ElasticSearch优化实战:打造高性能搜索引擎的秘籍

在当今这个大数据时代&#xff0c;信息的海量增长对搜索技术提出了前所未有的挑战。用户不仅需要快速准确地从数以亿计的数据中找到所需信息&#xff0c;还希望搜索引擎能够提供个性化和智能化的搜索体验。ElasticSearch作为市场上领先的搜索引擎&#xff0c;因其强大的全文搜索…

Typora2024最新版破解方法(亲测可用)

此方法非常简单&#xff0c;无需安装dll补丁&#xff0c;无需修改注册表&#xff0c;无需使用老版本。仅需修改部分文件内容即可 方法步骤 步骤一 下载并安装Typora 安装Typora 打开官网 下载并安装最新版即可 点击访问Typora官网 https://typoraio.cn/ 步骤二 修改文件 …

java面向对象重点总结

文章目录 java面向对象重点总结类与实例构造方法方法重载属性与修饰符封装继承多态重构抽象类接口抽象类和接口的区别&#xff1a;集合泛型 java面向对象重点总结 对象是一个自包含的实体&#xff0c;用一组可识别的特性和行为来标识。 面向对象编程&#xff0c;英文叫Object…

mcasttest-tool组播检测工具

作者&#xff1a;广大 检测组播 mcasttest-tool是oracle组播检测工具&#xff0c;组播是oracle 11.2.0.2开始的新功能。 1、上传mcasttest工具解压并授权 [rootrac1 soft]# cd /u01/soft/ [rootrac1 soft]# tar -xvf mcasttest.tgz[rootrac1 soft]# chown -R grid:oinstall…

Animate软件基础:“分散到图层”创建的新图层

FlashASer&#xff1a;AdobeAnimate2021软件零基础入门教程https://zhuanlan.zhihu.com/p/633230084 FlashASer&#xff1a;实用的各种Adobe Animate软件教程https://zhuanlan.zhihu.com/p/675680471 FlashASer&#xff1a;Animate教程及作品源文件https://zhuanlan.zhihu.co…