【C++进阶学习】第十一弹——C++11(上)——右值引用和移动语义
前言:
前面我们已经将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是一个已经存在的对象,用它来接受函数返回值的时候编译器就无法再优化了,所以会在移动构造后创建一个临时变量,且这个临时变量会被编译器识别为右值,从而调用移动赋值
四、总结
上面我们就简单的先提了一下右值引用的应用:移动语义,下一篇我们再重点讲解一下右值引用的另一个重点语法:完美挥发

相关文章:
【C++进阶学习】第十一弹——C++11(上)——右值引用和移动语义
前言: 前面我们已经将C的重点语法讲的大差不差了,但是在C11版本之后,又出来了很多新的语法,其中有一些作用还是非常大的,今天我们就先来学习其中一个很重要的点——右值引用以及它所扩展的移动定义 目录 一、左值引用和…...
JavaScript 监听 localStorage 的变化
使用 JavaScript 监听 localStorage 的变化 在Web开发中,localStorage是一种非常常用的本地存储机制。它允许我们在浏览器中存储键值对数据,即使用户关闭了浏览器或刷新页面,数据也不会丢失。但是,有时我们需要实时监控 localStorage 的变化,以便能够及时做出响应。在本文中,我…...
Java 中 HashMap 和 Hashtable 的联系
目录 相同 不同 1. 继承的父类不同 2. 线程安全性不同 3. 包含的 contains 方法不同 4. toString方法不同 5. 是否允许null值不同 6. 计算hash值的方式不同 7. 计算索引位置的方法不同 8. 初始化容量不同 9. 扩容方式不同 10. 内部存储策略不同(此处讨论…...
Web3 开发教程
引言 Web3 是指第三代互联网,其核心特征之一是去中心化。通过区块链技术和智能合约,Web3 应用程序(dApps)能够在无需中心化服务器的情况下运行。本文将引导你完成一个简单的 Web3 应用程序的开发过程,包括环境搭建、智…...
傻瓜式PHP-Webshell免杀学习手册,零基础小白也能看懂
项目描述 一、PHP相关资料 PHP官方手册: https://www.php.net/manual/zh/ PHP函数参考: https://www.php.net/manual/zh/funcref.php 菜鸟教程: https://www.runoob.com/php/php-tutorial.html w3school: https://www.w3school…...
第十九次(安装nginx代理tomcat)
回顾 1.安装nodejs---jdk一样你的软件运行环境 yum -y list install|grep epel $? yum -y install nodejs #版本号 node -v 2.下载对应的nodejs软件npm yum -y install npm npm -v npm set config ...淘宝镜像 3.安装vue/cli command line interface 命令行接口 npm ins…...
小红书0510笔试-选择题
Cache-Control:这是一个用于定义缓存行为的头部字段,它可以设定多个值来控制缓存的各个方面,如“public”、“private”、“no-cache”、“max-age”等。虽然Cache-Control的max-age指令可以指定缓存项的有效期,但它并不直接标识资…...
3.Java面试题之AQS
1. 写在前面 AQS(AbstractQueuedSynchronizer)是Java并发包(java.util.concurrent)中的一个抽象类,用于实现同步器(如锁、信号量、栅栏等)。AQS提供了一种基于FIFO队列的机制来管理线程的竞争和…...
redis的集群(高可用)
redis集群的三种模式: 主从复制 奇数 三台 一主两从 哨兵模式 3 一主两从 cluster集群 六台 主从复制:和mysql的主从复制类似,主可以写,写入主的数据通过RDB方式把数据同步到从服务器,从不能更新到主,也…...
随机森林的算法
1、随机森林算法简介 随机森林算法(Random Forests)是LeoBreiman于2001年提出的,它是一种通过重采样办法从原始训练样本集中有放回地重复随机抽取若干个样本生成多个决策树,样本的最终预测值由这些决策树的结果投票决定的一种有监督集成学习模型。 其核…...
3.1、数据结构-线性表
数据结构 数据结构线性结构线性表顺序存储和链式存储区别单链表的插入和删除练习题 栈和队列练习题 串(了解) 数据结构 数据结构该章节非常重要,上午每年都会考10-12分选择题下午一个大题 什么叫数据结构?我们首先来理解一下什…...
记一次对HTB:Carpediem的渗透测试
信息收集 端口扫描 通过nmap对靶机端口进行探测,发现存在22和80端口。 访问web页面。发现是一个静态页面,没有可利用的部分。 目录扫描 子域枚举 通过对域名进行fuzz子域名,发现存在portal一级域名。 将它加入/etc/hosts,访问之…...
MATH2 数据集:AI辅助生成高挑战性的数学题目
随着大型语言模型(LLMs)在理解和生成复杂数学内容方面的能力显著提高,通过利用所有公开数据以及相当一部分私有数据,已经取得了进展。然而,高质量、多样化和具有挑战性的数学问题来源正在逐渐枯竭。即使是寻找新的评估…...
加密货币“蓄势待发”!美国松口降息!九月开始连续降息8次?2025年利率目标3.25-3.5%?
今晨,美国联准会(Fed)结束FOMC会议,一如市场预期第八度冻涨利率在5.25%-5.5%。不过主席鲍威尔(Jerome Powell)在会后的记者会访出鸽派讯号,暗示9月降息脚步将近。这一消息令金融市场顿时沸腾,美股全面大涨&…...
Vue.js 3.x 必修课|005|代码规范与 ESLint 入门
欢迎关注公众号:CodeFit 创作不易,如果你觉得这篇文章对您有帮助,请不要忘了 点赞、分享 和 关注,为我的 持续创作 提供 动力! 1. 代码规范的重要性 在现代软件开发中,代码规范扮演着至关重要的角色。 特别是在团队协作的环境中,统一的代码风格可以大大提高工作效率和…...
【Linux】动态库|静态库|创建使用|动态库加载过程
目录 编辑 前言 静态库 为什么要使用库(形成原理 ) 生成一个静态库 静态库的使用 动态库 生成一个动态库 动态库的使用 解决方法 动态库加载过程 编辑 前言 库(Library)是一种方式,可以将代码打包成可重用的格式(站…...
WebSocket 协议与 HTTP 协议、定时轮询技术、长轮询技术
目录 1 为什么需要 WebSocket?2 WebSocket2.1 采用 TCP 全双工2.2 建立 WebSocket 连接2.3 WebSocket 帧 3 WebSocket 解决的问题3.1 HTTP 存在的问题3.2 Ajax 轮询存在的问题3.3 长轮询存在的问题3.4 WebSocket 的改进 参考资料: 为什么有 h…...
二叉树节点问题
问题:设一棵二叉树中有3个叶子结点,有8个度为1的结点,则该二叉树中总的结点数为( 13)个 设某种二叉树有如下特点:每个结点要么是叶子结点,要么有2棵子树。假如一棵这样的二叉树中有m(m>0&…...
公司里的IT是什么?
公司里的IT是什么? 文章目录 公司里的IT是什么?1、公司里的IT2、IT技术3、IT行业4、IT行业常见证书 如果对你有帮助,就点赞收藏把!(。・ω・。)ノ♡ 前段时间,在公…...
【小程序爬虫入门实战】使用Python爬取易题库
文章目录 1. 写在前面2. 抓包分析 【🏠作者主页】:吴秋霖 【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...
