右值引用带来的效率提升(C++11)

在这里插入图片描述

文章目录

  • 一.左值引用和右值引用
  • 二.C++11区分左值和右值的语法设计意义--对象的移动构造和移动赋值
    • 场景分析1:
      • C++11之前
      • C++11之后
    • 场景分析2:
    • 函数std::move
    • 右值引用的广泛使用
  • 三.引用折叠

一.左值引用和右值引用

  • 左值:可以取到地址的对象(可以出现在赋值符号的左边),对左值的引用称为左值引用&.
  • 右值(将亡值):无法取到地址的对象(不能出现在赋值符号的左边),对右值的引用称为右值引用&&.
    • 常见的右值有:字面常量,表达式返回值,函数返回值(非const限定的引用)(通常是常量或临时变量)
void Testreference1()
{//以下的p、b、c、*p都是左值//可以取到地址的变量int* p = new int(0);int b = 1;const int c = 2;//以下几个是对上面左值的左值引用int* & rp = p;int & rb = b;const int & rc = c;int & pvalue = *p;
}void Testreference2()
{double x = 1.1, y = 2.2;//以下几个都是对右值的右值引用//常量int&& rr1 = 10;//表达式返回值double&& rr2 = (x + y);//非const修饰的函数返回值(fmin返回值为double)double&& rr3 = fmin(x, y);
}
  • const type&(被const限定的左值引用)既可以引用左值,也可以引用右值

二.C++11区分左值和右值的语法设计意义–对象的移动构造和移动赋值

  • C++11区分出左值和右值是为了能够让编译器识别出一些即将析构的类对象(将亡),当这些类对象作为引用形参拷贝给其他对象时,通过右值的类型识别,就可以调用相应重载版本拷贝构造或赋值运算符重载来实现对象堆区资源的交换转移(通过交换指向堆区的指针实现),从而避免了没必要的深拷贝

场景分析1:

C++11之前

  • C++11之前的string对象模拟(只含对象的构造,拷贝构造,赋值重载,析构函数):
namespace mystring
{class string{public://构造函数(深拷贝)string(const char* str = ""):_size(strlen(str)),_capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}//交换两个string对象(堆区资源交换)void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::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(){delete[] _str;_str = nullptr;}private:char* _str;size_t _size;size_t _capacity; };
}
  • 执行以下代码:
string to_string()
{string tem("对象测试");return tem;
}
//tem在函数调用完后就是一个即将析构的对象
int main()
{string s(to_string());return 0;
}

在这里插入图片描述

  • 类似的场景下,在C++11之前,要接收tem的数据就会进行对象的深拷贝:在这里插入图片描述
  • tem完成拷贝后就会调用析构函数释放其内存资源,这种情况下tem的堆区资源其实是被浪费掉的,如果不进行深拷贝,直接将tem对象与s对象中的指针进行交换,就可以实现堆区资源的转移,但是这种指针交换操作会让被拷贝的对象无法再使用,因此就需要确定被拷贝的对象是否为即将析构的对象,于是C++区分出了左值和右值来识别一些即将析构的对象

C++11之后

  • C++11之后的string对象模拟(只含对象的构造,拷贝构造,赋值重载,析构函数):
namespace mystring
{class string{public://构造函数string(const char* str = ""):_size(strlen(str)),_capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}//交换两个string对象(堆区资源交换)void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::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){_str = new char[_capacity + 1];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;}private:char* _str;size_t _size;size_t _capacity; };
}
  • C++11之后类对象新增了两个默认成员函数:移动赋值移动构造函数(本质上就是构造函数赋值运算符重载函数形参为右值引用类型的重载版本)
  • 当类对象直接申请了堆区内存,移动赋值和移动构造函数就需要开发者自行将其实现用于对象拷贝的场景,实现方式一般就是交换两个对象指向堆区内存的指针.
  • 执行同样的代码:
string to_string()
{string tem("对象测试");return tem;
}
//tem在函数调用完后就是一个即将析构的对象
int main()
{//函数返回值被识别成右值,调用移动构造交换堆区资源,避免了深拷贝string s(to_string());return 0;
}

在这里插入图片描述

  • tem被识别成了右值(将亡值),s的创建调用了形参为右值引用的构造函数重载版本(也就是移动构造),进行了s对象和tem对象的堆区资源交换,避免了深拷贝在这里插入图片描述

场景分析2:

  • 使用前述的C++11之前的string对象C++11之后的string对象分别执行以下代码:
string to_string()
{string tem("对象测试");return tem;
}
//tem在函数调用完后就是一个即将析构的对象
int main()
{string s;s = to_string();return 0;
}

在这里插入图片描述

  • 上面类似的场景中,移动构造移动赋值避免了两次深拷贝

函数std::move

  • 当一个左值对象需要被拷贝并且拷贝完后不再使用,它作为对象拷贝函数的引用形参时,可以使用move函数将其强制识别为右值,比如:
int main()
{//测试构造函数string s1("对象测试");//s1是左值对象,调用拷贝构造string s2(s1);//move强制让编译器将s1识别成右值,调用移动构造交换堆区资源string s3(std::move(s1));cout << endl;//测试赋值重载函数//函数返回值被识别成右值,调用移动赋值交换堆区资源,避免了深拷贝string s4("对象测试");//s4是左值对象,调用赋值重载s2 = s4;//move强制让编译器将s1识别成右值,调用移动赋值,交换堆区资源s3 = std::move(s1);
}

在这里插入图片描述

右值引用的广泛使用

  • C++11之后,STL标准模板库中的所有数据结构对象中,所有可能涉及对象深拷贝的成员接口(包括对象的构造函数,赋值重载函数以及增删查改接口)都增加了形参为对象右值引用的重载版本,从而有效地减少了STL在使用的过程中对象深拷贝的次数.类对象移动构造和移动赋值的设计模式,一定程度上提高了C++代码的时间效率

三.引用折叠

  • 引用折叠的概念:作为类型模板参数的引用可以起到引用折叠的作用,既可以接收左值引用也可以接收右值引用
template<typename T>
void PerfectForward(T&& t)
{
}
  • 代码段中的函数形参t既可以接收左值引用也可以接收右值引用,但是当t接收到右值引用时,会将其转化为左值引用(底层机制是在内存中开一段空间存储对象),我们希望能够在引用传递的过程中保持它的左值或者右值的属性,这时就需要std::forward函数来辅助引用参数传递(称为完美转发):在这里插入图片描述
int main()
{string s1("对象测试");list<string> List;List.push_back(std::move(s1));
}
  • 上面的STL的使用场景中就发生了引用的完美转发,list的push_back接口的内部代码结构:
template<class T>
struct ListNode
{ListNode* _next = nullptr;ListNode* _prev = nullptr;T _data;
};
template<class T>
class list
{typedef ListNode<T> Node;
public://复用Insert实现void push_back(T&& x)//引用折叠{//引用完美转发Insert(_head, std::forward<T>(x));}//链表结点插入接口void Insert(Node* pos, T&& x)//引用折叠{Node* prev = pos->_prev;Node* newnode = new Node;//引用完美转发newnode->_data = std::forward<T>(x);prev->_next = newnode;newnode->_prev = prev;newnode->_next = pos;pos->_prev = newnode;}
private:Node* _head;
};

在这里插入图片描述

  • 最终s1的右值引用传递到了string对象的移动赋值函数中,避免了s1对象的深拷贝
    在这里插入图片描述

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

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

相关文章

【Linux】计算机网络的背景和协议分层

文章目录 网络发展协议何为协议网络协议协议分层OSI七层模型TCP/IP五层模型&#xff08;四层&#xff09; 基本通信流程mac地址和ip地址网络通信本质 网络发展 从一开始计算机作为一台台单机使用&#xff0c;到现在网络飞速发展&#xff0c;从局域网Lan建立起局域网&#xff0…

go逆向符号恢复

前言 之前一直没怎么重视&#xff0c;结果发现每次遇到go的题都是一筹莫展&#xff0c;刷几道题练习一下吧 准备 go语言写的程序一般都被strip去掉符号了&#xff0c;而且ida没有相关的签名文件&#xff0c;没办法完成函数名的识别与字符串的定位&#xff0c;所以第一步通常…

时序数据异常检测算法

引言 异常检测的场景很多&#xff0c;例如&#xff1a;硬件的故障检测、流量的异常点的检测等场景。针对时间序列类数据的异常检测算法也有很多&#xff0c;业界比较流行的比如普通的统计学习方法–3σ原则和箱线图识别数据离群点&#xff0c;它利用检测点偏移量来检测出异常。…

【八】mybatis 日志模块设计

mybatis 日志模块设计 简介&#xff1a;闲来无事阅读一下mybatis的日志模块设计&#xff0c;学习一下优秀开源框架的设计思路&#xff0c;提升自己的编码能力 模块设计 在Mybatis内部定义了4个级别&#xff1a;Error:错误 、warn:警告、debug:调试、trance&#xff0c;日志优…

HDFS架构刨析

HDFS架构刨析 概述HDFS架构图整体概述主角色&#xff1a;namenodefsimage内存元数据镜像文件edits log&#xff08;Journal&#xff09;编辑日志 从角色&#xff1a;datanode主角色辅助角色&#xff1a;secondarynamenode 重要特性主从架构分块存储机制副本机制namespace元数据…

iPhone 8透明屏的透明度高吗?

iPhone 8是苹果公司于2017年推出的一款智能手机&#xff0c;它采用了全新的设计和技术&#xff0c;其中一个亮点就是透明屏。 透明屏是指屏幕具有透明度&#xff0c;可以透过屏幕看到背后的物体。 iPhone 8的透明屏采用了最新的OLED技术&#xff0c;这种技术可以实现更高的对比…

后端技术趋势指南|如何选择自己的技术方向

编程多条路&#xff0c;条条通罗马 后台大佬 后台路线都是面对后台服务器业务&#xff0c;比如web后台服务器&#xff0c;视频后台服务器&#xff0c;搜索后台服务器&#xff0c;游戏后台服务器&#xff0c;直播后台服务器&#xff0c;社交IM后台服务器等等&#xff0c;大部分…

桶排序算法

桶排序算法 算法思想概述&#xff1a; 桶排序&#xff08;Bucket Sort&#xff09;是一种非比较性的排序算法&#xff0c;它将待排序的元素分到有限数量的桶&#xff08;或箱子&#xff09;中&#xff0c;然后对每个桶中的元素分别进行排序&#xff0c;最后合并所有桶的元素得…

使用tinyxml解析和修改XML文件

首先要清楚XML文件包含哪些元素&#xff1a; 他是由元素、文本或者两者混合物组成。元素可以拥有属性&#xff0c;元素是指从开始标签到结束标签的部分。 <?xml version"1.0" encoding"UTF-8" ?> <books><book id"1001">&…

Java版工程项目管理系统平台+企业工程系统源码+助力工程企业实现数字化管理 em

鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部工程…

TypeScript【enum 枚举】

导语 在 TypeScript 中&#xff0c;新增了很多具有特性的一些数据类型处理方法&#xff0c;enum 【枚举】就是其中&#xff0c;很具有代表性的一种&#xff0c;所以本章节就来聊聊 在 TypeScript 中如何去运用 enum 【枚举】。 枚举的概念&#xff1a; 枚举&#xff08;Enum&am…

yolov3-tiny原理解析及代码分析

前言 从去年十一月份开始学习yolo神经网络用于目标识别的硬件实现&#xff0c;到现在已经六个月了。一个硬件工程师&#xff0c;C/C基础都差劲的很&#xff0c;对照着darknet作者的源码和网上东拼西凑的原理讲解&#xff0c;一点一点地摸索。刚开始进度很慢&#xff0c;每天都…

汽车EBSE测试流程分析(四):反思证据及当前问题解决

EBSE专题连载共分为“五个”篇章。此文为该连载系列的“第四”篇章&#xff0c;在之前的“篇章&#xff08;三&#xff09;”中已经结合具体研究实践阐述了“步骤二&#xff0c;通过系统调研确定改进方案”等内容。那么&#xff0c;在本篇章&#xff08;四&#xff09;中&#…

怎么维护好自己的电脑

你的电脑已经成为你工作、学习、娱乐的最佳工具之一&#xff0c;但是如果你不做好电脑维护工作&#xff0c;就可能面临着电脑变慢、蓝屏、崩溃等问题。在这篇文章中&#xff0c;我们将介绍10个电脑维护步骤&#xff0c;让你的电脑更加稳定&#xff01; 为什么需要电脑维护&…

2023年华数杯数学建模B题思路代码分析 - 不透明制品最优配色方案设计

# 1 赛题 B 题 不透明制品最优配色方案设计 日常生活中五彩缤纷的不透明有色制品是由着色剂染色而成。因此&#xff0c;不透明 制品的配色对其外观美观度和市场竞争力起着重要作用。然而&#xff0c;传统的人工配色 存在一定的局限性&#xff0c;如主观性强、效率低下等。因此…

Docker Dockerfile 语法与指令

一、简介 Docker 镜像原理、容器转成镜像 随便找个案例&#xff0c;进入 https://hub.docker.com/ 搜索 centos&#xff0c;然后随便找个版本&#xff08;例如&#xff1a;centos7&#xff09;点击一下&#xff0c;就会进入 centos7 的 dockerfile 文件&#xff1a; // 空镜像…

基于 Redux + TypeScript 实现强类型检查和对 Json 的数据清理

基于 Redux TypeScript 实现强类型检查和对 Json 的数据清理 突然像是打通了任督二脉一样就用了 generics 搞定了之前一直用 any 实现的类型…… 关于 Redux 的部分&#xff0c;这里不多赘述&#xff0c;基本的实现都在这里&#xff1a;Redux Toolkit 调用 API 的四种方式 和…

【Leetcode】73.矩阵置零

一、题目 1、题目描述 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例1: 输入:matrix = [[1,1,1],[1,0,1],[1,1,1]] 输出:[[1,0,1],[0,0,0],[1,0,1]]示例2: 输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1…

2023年华数杯数学建模C题思路代码分析 - 母亲身心健康对婴儿成长的影响

# 1 赛题 C 题 母亲身心健康对婴儿成长的影响 母亲是婴儿生命中最重要的人之一&#xff0c;她不仅为婴儿提供营养物质和身体保护&#xff0c; 还为婴儿提供情感支持和安全感。母亲心理健康状态的不良状况&#xff0c;如抑郁、焦虑、 压力等&#xff0c;可能会对婴儿的认知、情…

深入了解PostgreSQL:高级查询和性能优化技巧

在当今数据驱动的世界中&#xff0c;数据库的性能和查询优化变得尤为重要。 POSTGRESQL作为一种开源的关系型数据库管理系统&#xff0c;在处理大规模数据和复杂查询时表现出色。 但随着数据量和查询复杂性的增加&#xff0c;性能问题可能会显现出来。 本文将深入探讨POSTGR…