C++之完美转发、移动语义(forward、move函数)
完美转发
1. 在函数模板中,可以将自己的参数“完美”地转发给其它函数。所谓完美,即不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变。
2. C++11标准引入了右值引用和移动语义,所以,能否实现完美转发,决定了该参数在传递过程使用的是拷贝语义(调用拷贝构造函数)还是移动语义 (调用移动构造函数)。
1. 如果模板中 (包括类模板和函数模板)函数的参数书写成为T&& 参数名 那么,函数既可以接受左值引用,又可以接受右值引用。
2. 提供了模板函数std::forward<T>(参数),用于转发参数如果参数是一个右值,转发之后仍是右值引用;如果 参数是一个左值,转发之后仍是左值引用
move
在我的另一篇文章《C++之右值引用、移动构造函数》提到,左值不能初始化右值引用。
那么我们能不能用左值初始化一个右值引用呢??这里C++给我们了一个函数move();
move()函数
将左值转换为右值,实现对象资源的转移。
下面看个例子
void test2()
{vector<int> v;for (int i = 0; i < 10; i++)v.push_back(i);vector<int>vv = v; //拷贝cout << "v =";for (auto x : v)cout << x << " ";cout << ", v.addr=" << &v << endl;cout << "vv =";for (auto x : vv)cout << x << " ";cout << ", vv.addr=" << &vv << endl;cout << "after move v to vvv" << endl;vector<int>vvv = move(v);cout << "vvv=";for (auto x : vvv)cout << x << " ";cout << ",vvv.addr=" << &vvv << endl;cout << "v =";for (auto x : v)cout << x << " ";cout << ", v.addr=" << &v << endl;
}
forward
右值引用类型是独立于值的,一个右值引用作为函数参数的形参时,在函数内部转发该参数给内部其他函数时,它就变成一个左值(因为没有实名的右值被编译器视为左值),并不是原来的类型了。如果需要按照参数原来的类型转发到另一个函数,可以使用 C++11 提供的 std::forward () 函数,该函数实现的功能称之为完美转发。
#include<iostream>
using namespace std;
template<typename T>
void PrintT(T& t)
{cout << "lvalue" << endl;
}
template<typename T>
void PrintT(T&& t)
{cout << "rvalue" << endl;
}
template<typename T>
void TestForward(T&& v)
{PrintT(v);PrintT(move(v));PrintT(forward<T>(v));cout << endl;
}
int main()
{TestForward(1);int x = 1;TestForward(x);TestForward(forward<int>(x));TestForward(forward<int&>(x));TestForward(forward<int&&>(x));return 0;
}
分析一下
TestForward(1);实参为右值,T&&->右值引用
1. PrintT(v);已经实名的右值编译器当成左值处理,实参->左值
2. PrintT(move(v));move将左值转换为右值,实参->右值
3. PrintT(forward<T>(v));模板参数为右值引用,最终得到一个右值,实参->右值
TestForward(x);实参为左值,T&&->左值引用
1. PrintT(v);实参->左值
2. PrintT(move(v));move将左值转换为右值,实参->右值
3. PrintT(forward<T>(v));模板参数为左值引用,最终得到一个左值,实参->左值
TestForward(forward<int>(x));模板参数类型为int->右值,最终得到右值,T&&->右值引用
1. PrintT(v);已经实名的右值编译器当成左值处理,实参->左值
2. PrintT(move(v));move将左值转换为右值,实参->右值
3. PrintT(forward<T>(v));模板参数为右值引用,最终得到一个右值,实参->右值
TestForward(forward<int&>(x));模板参数类型为左值引用,最终得到一个左值,T&&->左值引用
1. PrintT(v);实参->左值
2. PrintT(move(v));move将左值转换为右值,实参->右值
3. PrintT(forward<T>(v));模板参数类型为左值引用,最终得到一个左值,实参->左值
TestForward(forward<int&&>(x));模板参数类型为右值引用,最终得到一个右值,T&&->右值引用
1. PrintT(v);已经实名的右值编译器当成左值处理,实参->左值
2. PrintT(move(v));move将左值转换为右值,实参->右值
3. PrintT(forward<T>(v));模板参数类型为右值引用,最终得到一个右值,T&&->右值引用
移动语义
如果一个对象中有堆区资源,需要编写拷贝构造函数和赋值函数,实现深拷贝。
深拷贝把对象中的堆区资源复制了一份,如果源对象(被拷贝的对象)是临时对象,拷贝完就没什么用了,这样会造成没有意义的资源申请和释放操作。如果能够直接使用源对象拥有的资源,可以节省资源申请和释放的时间。C++11 新增加的移动语义就能够做到这一点。
下面给出示例:
我们当时提到的深浅拷贝,深拷贝解决了浅拷贝造成的内存泄露问题,但是反复的构造和释放对象拉低了程序的效率。所以我们在想是否能直接使用原对象的资源,这样可以大大提高效率
#include<iostream>
using namespace std;
class AA
{
public:int* m_data = nullptr;AA() = default;void alloc(){m_data = new int;memset(m_data, 0, sizeof(int)); //初始化已分配的内存}AA(const AA& a) //拷贝构造函数{cout << "调用了拷贝构造函数" << endl;if (m_data == nullptr) //如果没有分配内存,就分配alloc();memcpy(m_data, a.m_data, sizeof(int));}AA(AA&& a) //移动构造函数{cout << "调用了移动构造函数" << endl;if (m_data != nullptr) //如果分配内存,释放掉delete m_data;m_data = a.m_data;a.m_data = nullptr;}AA& operator=(const AA& a){cout << "调用了赋值函数" << endl;if (this == &a) //避免自我复制return *this;if (m_data == nullptr) //如果没有分配内存,就分配alloc();memcpy(m_data, a.m_data, sizeof(int));return *this;}AA& operator=(AA&& a){cout << "调用了移动赋值函数" << endl;if (this == &a) //避免自我复制return *this;if (m_data != nullptr) //如果分配内存,释放掉delete m_data;m_data = a.m_data;a.m_data = nullptr;return *this;}~AA(){if (m_data != nullptr)delete m_data;m_data = nullptr;}
};
测试案例1
void test1()
{cout << "test1-begin" << endl;AA a1;a1.alloc();*a1.m_data = 3;cout << "a1.m_data=" << *a1.m_data << endl;AA a2(a1);cout << "a2.m_data=" << *a2.m_data << endl;AA a3;a3 = a1;cout << "a3.m_data=" << *a3.m_data << endl;cout << "test1-end" << endl << endl;
}
这是一组普通的测试案例,返回结果
注意:
对于一个左值,会调用拷贝构造函数,但是有些左值是局部变量,生命周期也很短,能不能也移动而不是拷贝呢?C++11为了解决这个问题,提供了std:move0方法来将左值转义为右值,从而方便使用移动语义。它其实就是告诉编译器,虽然我是一个左值,但不要对我用拷贝构造函数,用移动构造函数吧。左值对象被转移资源后,不会立刻析构,只有在离开自己的作用域的时候才会析构,如果继续使用左值中的资源,可能会发生意想不到的错误。
如果没有提供移动构造/赋值函数,只提供了拷贝构造/赋值函数,编译器找不到移动构造/赋值函数就去寻找拷贝构造/赋值函数。
C++11中的所有容器都实现了移动语义,避免对含有资源的对象发生无谓的拷贝。
移动语义对于拥有资源(如内存,文件句柄)的对象,如果是基本类型,使用移动语义没有意义。
测试案例2
void test2()
{cout << "test2-begin" << endl;AA a1;a1.alloc();*a1.m_data = 3;AA a6(std::move(a1));cout << "a6.m_data=" << *a6.m_data << endl;AA a7;a7 = std::move(a1);cout << "a7.m_data=" << *a7.m_data << endl;cout << "test2-end" << endl << endl;
}
返回结果
test2-begin
调用了移动构造函数
a6.m_data=3
调用了移动赋值函数
//程序到这里崩溃了为什么呢?
这里就是因为a1被转移资源后,下面再次对a1进行转移资源导致报错。
测试案例3
void test3()
{cout << "test3-begin" << endl;auto f = [] {AA aa; aa.alloc(); *aa.m_data = 8; return aa; };AA a4 = f();cout << "a4.m_data=" << *a4.m_data << endl;AA a5;a5 = f();cout << "a5.m_data=" << *a5.m_data << endl;cout << "test3-end" << endl << endl;
}
返回结果
test3-begin
调用了移动构造函数
a4.m_data=8
调用了移动构造函数
调用了移动赋值函数
a5.m_data=8
test3-end
相关文章:
C++之完美转发、移动语义(forward、move函数)
完美转发1. 在函数模板中,可以将自己的参数“完美”地转发给其它函数。所谓完美,即不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变。2. C11标准引入了右值引用和移动语义,所以,能否实现完美转发&…...
LeetCode刷题系列 -- 48. 旋转图像
给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。示例 1:输入:matrix [[1,2,3],[4,5,6],[7,8,9]]输出&#…...
在多线程环境下使用哈希表
一.HashTable和HashMapHashTable是JDK1.0时创建的,其在创建时考虑到了多线程情况下存在的线程安全问题,但是其解决线程安全问题的思路也相对简单:在其众多实现方法上加上synchronized关键字(效率较低),保证…...
【排序算法】堆排序(Heap Sort)
堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序介绍学习堆排序之前,有必要了解堆!若…...
分类预测 | Matlab实现SSA-RF和RF麻雀算法优化随机森林和随机森林多特征分类预测
分类预测 |Matlab实现SSA-RF和RF麻雀算法优化随机森林和随机森林多特征分类预测 目录分类预测 |Matlab实现SSA-RF和RF麻雀算法优化随机森林和随机森林多特征分类预测分类效果基本介绍模型描述程序设计参考资料分类效果 基本介绍 Matlab实现SSA-RF和RF麻雀算法优化随机森林和随机…...
Allegro如何添加ICT操作指导
Allegro如何添加ICT操作指导 当PCB板需要做飞针测试的时候,通常需要在PCB设计的时候给需要测试的网络添加上ICT。 如图: Allegro支持给网络添加ICT,具体操作如下 首先在库中创建一个阻焊开窗的过孔,比如via10-ict一般阻焊开窗的尺寸比盘单边大2mil 在PCB中选择Manufacture…...
软件架构设计(二)——领域架构、基于架构的软件开发方法
目录 一、架构描述语言 ADL 二、特定领域软件架构 DSSA 三、DSSA的三层次架构模型 . 四、基于架构的软件开发方法 (1)基于架构的软件设计(ABSD) (2)开发过程 一、架构描述语言 ADL ADL是一种形式化语言,它在底层语义模型的支持下,为软件系统概念体…...
数组常用方法(2)---数组遍历方法
1. forEach(cb) 回调函数中有三个参数,第一个是当前遍历项(必须),第二个是索引,第三个是遍历的数组本身。forEach() 对于空数组不会执行回调函数。forEach()不会使用回调函数的返回值,返回值为undefined。…...
卸载Node.js
0 写在前面 无论您是因为什么原因要卸载Node.js都必须要卸载干净。 请阅读: 1 卸载步骤 1.1通过控制面板卸载node.js winR—>control.exe—>卸载程序—>卸载Node.js 等待—>卸载成功 1.2 删除安装时的nodejs文件夹 通过记忆或者Everthing搜索找…...
发表计算机SCI论文,会经历哪些过程? - 易智编译EaseEditing
一、选期刊。 一定要先选期刊。每本期刊都有自己的特色和方向,如果你的稿子已经成型,再去考虑期刊选择的问题,恐怕后期不是退稿就是要大面积修改稿子。 选期刊的标准没有一定的,主要是各单位都有自己的要求,当然小编…...
python中lambda的用法
1. lambada简单介绍 lambda 在Python编程中使用的频率非常高,我们通常提及的lambda表达式其实是python中的一类特殊的定义函数的形式,使用它可以定义一个匿名函数。即当你需要一个函数,但又不想费神去命名一个函数,这时候…...
网络安全协议(3)
作者简介:一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页 目录 前言 一.当前流行操作系统的安全等级 1.Windows的安全等级 什么是EAL…...
102.第十九章 MySQL数据库 -- MySQL的备份和恢复(十二)
5.备份和恢复 5.1 备份恢复概述 5.1.1 为什么要备份 灾难恢复:硬件故障、软件故障、自然灾害、黑客攻击、误操作测试等数据丢失场景 参考链接: https://www.toutiao.com/a6939518201961251359/ 5.1.2 备份类型 完全备份,部分备份 完全备份:整个数据集 部分备份:只备份数…...
【C++】C++入门 类与对象(一)
类与对象(一)一、类的引入二、类的定义1、类的两种定义方式:2、成员变量命名规则的建议:三、类的访问限定符及封装1、访问限定符2、封装四、类的实例化1、类的实例化概念2、类对象的大小的计算五、this指针this指针的特性一、类的…...
笔记_js运算符
目录二进制相关运算符移位运算符<<>>|(位或运算)参考文档二进制相关运算符 移位运算符 移位运算就是对二进制进行有规律的移位。 tips:进制转换文档链接 << “<<”运算符执行左移位运算。在移位运算过程中,符号位始终保持不变…...
java面试题(十九) Mybatis
4.1 谈谈MyBatis和JPA的区别 参考答案 ORM映射不同: MyBatis是半自动的ORM框架,提供数据库与结果集的映射; JPA(默认采用Hibernate实现)是全自动的ORM框架,提供对象与数据库的映射。 可移植性不同&…...
Linux系统位运算函数以及相应CPU ISA实现收录
以32位数据的二进制表示为例,习惯的写法是LSB在左,MSB在右,注意BIT序和大小端的字节序没有关系。Linux和BIT操作有关的接口在定义在头文件bitops.h中,bitops.h定义有两层,通用层和架构层,对应两个bitops.h&…...
logback配置文件---logback.xml
目录常识操作logback-spring.xml 示例参考于 https://blog.csdn.net/white_ice/article/details/85065219 https://blog.csdn.net/weixin_42592282/article/details/122109703 https://www.dianjilingqu.com/629077.html 常识 https://www.dianjilingqu.com/629077.html nod…...
Web前端-设计网站公共header
设计网站公共headerheader元素是一个具有引导和导航作用的结构元素,很多企业网站中都有一个非常重要的header元素,一般位于网页的开头,用来显示企业名称、企业logo图片、整个网站的导航条,以及Flash形式的广告条等。在本网站中&am…...
引用和指针傻傻分不清
🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀 目录 🐰引用和指针的区别 🌸从现象上看 🌸从编译上看 &am…...
MySQL面试题:关系型数据库SQL和非关系型数据库NoSQL
文章目录一、四大非关系型数据库与关系型数据库的对比1. 关系型数据库2. 基于列的数据库3. 键值对存储4. 文档存储5. 图形数据库参考文章(金文):四大非关系型数据库类型,你知道多少 参考文章:“行式存储”和“列式存储…...
1.Redis【介绍与安装】
1.常用数据库介绍 mysql的表类型[表引擎.存储引擎],memory表结构和表数据分开存储的,表结构保存在硬盘中,表数据保存在内存中memcache是一款软件,可以使用键值对的格式保存数据到内存中redis是意大利的工程师开发的开源免费的告诉缓存数据库,需要注意的是作者本身只开发了linu…...
DataStore快速上手1-preference
DataStore 概念 DataStore 可以存储两种类型的数据,一种是 preference,一种是 protobuf 每个进程在同一时间内仅能打开一个 DataStore 实例(或者通过其他管理手段来实现多个 DataStore 交替使用) 一个 DataStore 可以视为一张数…...
彻底掌握 MySQL InnoDB 的锁机制
本文是对沈剑大佬锁机制十多篇文章的概括总结,文末有全部链接,还参考了 10 多位其他网友的优秀分享。 1、概要 MySQL 中的锁可以按照粒度分为锁定整个表的表级锁(table-level locking)和锁定数据行的行级锁(row-level locking): 表级锁具有开…...
C++继承
1.继承的概念及定义 1.1继承的概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构&#x…...
动态代理是基于什么原理?
第6讲 | 动态代理是基于什么原理? 编程语言通常有各种不同的分类角度,动态类型和静态类型就是其中一种分类角度,简单区分就是语言类型信息是在运行时检查,还是编译期检查。 与其近似的还有一个对比,就是所谓强类型和弱…...
YOLO-V4经典物体检测算法介绍
在前文我们介绍了YOLO-V1~V3版本都做了哪些事,本文我们继续介绍YOLO-V4版本。YOLO的作者在发表完V3之后,发现YOLO产品被美国军方应用到了很多军事战争当中,这是他所不希望看见的,因此宣布不再继续研究。但历史和科技总是随时间不断…...
angular相关知识点总结
创建 angualr 组件和传值 angular组件其实就是个xxx.component.ts,本质还是ts文件一个html文件 1.创建组件:在Angular中,可以使用命令行工具ng generate component创建一个新组件。例如: ng generate component my-component这将创建一个名…...
大坝安全监测系统:水库“守坝人”!
一、项目背景 随着社会经济的迅速发展,我国水资源利用率越来越高,各类水利水电工规模进一步扩大。在抗洪救灾、水利发电等方面带来巨大的经济和社会效益。但受多种因素影响,大坝的安全问题日益严重。大量工程实践证明,为保证大坝…...
CentOS7安装配置OpenVNP连接远端服务器
在项目当中需要访问一个三方接口及数据库,但是需要在CentOS7服务器上先配置OpenVPN,然后才能连接,现将整体配置过程记录如下。 安装 yum -y install epel-release yum -y install openvpn 查看版本 openvpn --version 配置客户端证书 打开…...
佛山专业的网站建设公司/中国搜索引擎排行榜
博文原创,转载请声明出处蓝岩--移动互联网老兵IOS中,如果要在主线程中启动一个子线程,可以又两种方法: [NSThread detachNewThreadSelector:selector(myThreadMainMethod:) toTarget:self withObject:nil];这是在cocoa早期提供的方…...
学做日本料理网站/百度学术搜索
CFAR(自适应常量反射率)算法是一种用于检测弱信号的算法,它使用自适应的反射率确定信号的强度,从而检测到弱信号。它利用一种称为“自适应参考窗口”的技术,其中每个测量点都有一个参考窗口,其大小由测量点的邻居决定。算法将测量…...
wordpress网站怎么打开/淘宝seo关键词的获取方法有哪些
http://www.jianshu.com/p/7d4710b815c2转载于:https://www.cnblogs.com/wcLT/p/4650283.html...
手机优化怎么关闭/旺道seo软件
维思营销策划祝大家国庆—中秋节快乐维思营销策划祝大家国庆—中秋节快乐维思营销策划祝大家国庆—中秋节快乐维思营销策划祝大家国庆—中秋节快乐维思营销策划祝大家国庆—中秋节快乐维思营销策划祝大家国庆—中秋节快乐维思营销策划祝大家国庆—中秋节快乐维思营销策划祝大家…...
聊城网站建设的地方/站内seo和站外seo区别
咸鱼ZTMR实例—按键用法在pyboard上,有一个用户按键。MicroPython已经预先定义好了按键的类,按键可以这样使用: 定义按键 sw pyb.Switch() 读取按键状态 sw() 定义按键回调函数 sw.callback(lambda:pyb.LED(1).toggle()) 禁用按键回调函数…...
无锡微网站/常见的网络营销手段
在人工智能领域,在网络爬虫、服务器开发、3D游戏、图形界面开发、网络编程、数据分析、Web开发、金融、运维、测试等多个领域,Python都有不俗的表现。而我们需要掌握的知识点也很多,实战技巧复杂。 Python的语法非常接近自然语言(英语)&#…...