【C++基础】左值引用、右值引用、move、forward

 本文参考:右值引用 | 爱编程的大丙

转移和完美转发 | 爱编程的大丙 

左值、右值、左值引用、右值引用

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

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

左值引用必须接收左值,同理右值引用必须接收右值,否则会报错

左值常量引用和右值常量

在讲左值常量引用和右值常量引用之前,先讲讲什么是常量引用?

常量引用是一种特殊的引用,声明了一个常量引用后,不可以通过此引用去修改原本变量的值了,但是直接修改原变量还是依然可以的。

常量右值引用接收的值必须是右值,注意,当常量右值引用进行传递就会变成左值即编译器会将已命名的右值引用视为左值,将未命名的右值引用视为右值。所以下面代码中  const int&& e = b; 会产生报错,因为此时b为左值

常量左值引用是一个万能的引用类型,它可以接收各种类型的数据,包括左值、右值、左值引用、右值引用、常量左值引用、常量右值引用。

int main()
{//左值: 可以取地址的指int num = 9;//左值引用int& a = num;int& l = 9;  //error,必须接收左值//右值:不可以取到地址的指8;//右值引用int&& b = 8;int&& c = b;  //error,必须接收的是右值//常量右值引用const int&& d = 6;const int&& e = b;   //error,必须接收的是右值//常量左值引用const int& f = num; //常量左值引用可以等于左值const int& j = 8;   //常量左值引用可以等于右值const int& k = a;   //常量左值引用可以等于左值引用const int& g = b;   //常量左值引用可以等于右值引用const int& h = f;   //常量左值引用可以等于常量左值引用const int& i = d;   //常量左值引用可以等于常量右值引用//所以称常量左值引用是一个万能的类型,可以接收各种类型的数据
}

移动构造函数

右值引用的重要作用之一 —— 移动构造函数,带来性能优化

在C++中在进行对象赋值操作的时候,很多情况下会发生对象之间的深拷贝,如果堆内存很大,这个拷贝的代价也就非常大,在某些情况下,如果想要避免对象的深拷贝,就可以使用右值引用进行性能的优化。

若不使用移动构造函数:

#include <iostream>
using namespace std;class Test
{
public:Test() : m_num(new int(100)){cout << "construct: my name is jerry" << endl;}Test(const Test& a) : m_num(new int(*a.m_num)){cout << "copy construct: my name is tom" << endl;}~Test(){delete m_num;}int* m_num;
};Test getObj()
{Test t;return t;
}int main()
{Test t = getObj();cout << "t.m_num: " << *t.m_num << endl;return 0;
};

输出结果:vs2019 (vs2022中已被优化)

construct: my name is jerry
copy construct: my name is tom
t.m_num: 100

通过输出的结果可以看到调用  Test t = getObj();  的时候调用拷贝构造函数对返回的临时对象进行了深拷贝得到了对象t,在getObj()函数中创建的对象虽然进行了内存的申请操作,但是没有使用就释放掉了。

如果能够使用临时对象已经申请的资源,既能节省资源,还能节省资源申请和释放的时间,如果要执行这样的操作就需要使用右值引用了,右值引用具有移动语义,移动语义可以将资源(堆、系统对象等)通过浅拷贝从一个对象转移到另一个对象这样就能减少不必要的临时对象的创建、拷贝以及销毁,可以大幅提高C++应用程序的性能。

#include <iostream>
using namespace std;class Test
{
public:Test() : m_num(new int(100)){cout << "construct: my name is jerry" << endl;}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 = nullptr;cout << "move construct: my name is sunny" << endl;}~Test(){delete m_num;cout << "destruct Test class ..." << endl;}int* m_num;
};Test getObj()
{Test t;return t;
}int main()
{Test t = getObj();cout << "t.m_num: " << *t.m_num << endl;return 0;
};

 输出结果:vs2019 (vs2022中已被优化)

construct: my name is jerry
move construct: my name is sunny
destruct Test class ...
t.m_num: 100
destruct Test class ...

通过修改,在上面的代码给Test类添加了移动构造函数(参数为右值引用类型),这样在进行Test t = getObj();操作的时候并没有调用拷贝构造函数进行深拷贝,而是调用了移动构造函数,在这个函数中只是进行了浅拷贝,没有对临时对象进行深拷贝,提高了性能。

在测试程序main()中getObj()的返回值就是一个将亡值,也就是说是一个右值,在进行赋值操作的时候如果=右边是一个右值,那么移动构造函数就会被调用。移动构造中使用了右值引用,会将临时对象中的堆内存地址的所有权转移给对象t,这块内存被成功续命,因此在t对象中还可以继续使用这块内存。

对于需要动态申请大量资源的类,应该设计移动构造函数,以提高程序效率。

关于&&

并不是所有情况下 && 都代表是一个右值引用,具体的场景体现在模板和自动类型推导中,如果是模板参数需要指定为T&&,如果是自动类型推导需要指定为auto &&,在这两种场景下 &&被称作未定的引用类型。

通过右值推导 T&& 或者 auto&& 得到的是一个右值引用类型

通过非右值(右值引用、左值、左值引用、常量右值引用、常量左值引用)推导 T&& 或者 auto&& 得到的是一个左值引用类型

move

使用std::move方法可以将左值转换为右值。使用这个函数并不能移动任何东西,而是和移动构造函数一样都具有移动语义,将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存拷贝。

其主要有两个作用

1、将左值转换为右值,用于初始化右值引用

class Test
{
public:Test(){}......
}
int main()
{Test t;Test && v1 = t;          // errorTest && v2 = move(t);    // okreturn 0;
}

2、一个对象内部有较大的堆内存或者动态数组时,使用move()可以方便的进行数据所有权的转移

list<string> ls;
ls.push_back("hello");
ls.push_back("world");
......
list<string> ls1 = ls;        // 需要拷贝, 效率低
list<string> ls2 = move(ls);

 forward 完美转发

在 C++11 引入,并被用于解决函数模板中的参数传递问题。

 forward 用于在一个函数模板中将参数按照原始的值类别(左值或右值)转发给另一个函数。这意味着 forward 会保留参数传递时的左值或右值属性,使得接收函数能够正确地处理这些参数。

std::forward<T>(t);

当T为左值引用类型时,t将被转换为T类型的左值
当T不是左值引用类型时,t将被转换为T类型的右值

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

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

相关文章

UNext:基于 MLP 的快速医学图像分割网络

UNext 会议分析摘要贡献方法整体框架1.Shifted MLP2.Tokenized MLP Stage 实验1.对比实验2.消融实验2.1 模块的消融实验2.2 通道数的探索 可借鉴参考 会议分析 期刊&#xff08;会议&#xff09;名&#xff1a; MICCAI 2022 会议信息&#xff1a; 属于顶会了 有代码&#xff1…

3D视觉测量:3D空间圆拟合优化

文章目录 0. 测试效果1. 测量思路2. 部分调用代码文章目录:形位公差测量关键内容:通过视觉方法实现GD&T中的圆孔位置定位0. 测试效果 1. 测量思路 空间圆拟合是指在三维空间中找到最佳拟合圆的过程。这通常用于从一组三维点数据中找到一个圆,以最小化数据点到圆的距离之…

百度地图3D棱柱鼠标事件

百度地图2D API JavaScript API | 百度地图API SDK 百度地图3D API jspopularGL | 百度地图API SDK 3D棱柱效果如下 一. 渲染地图 var map new BMapGL.Map(container, {style: {styleJson: styleJson2} }) map.centerAndZoom(new BMapGL.Point(116.404, 39.925), 9); map…

通过rabbitmq生成延时消息,并生成rabbitmq镜像

通过rabbitmq生成延时消息队列&#xff0c;并生成rabbitmq镜像 整体描述1. 使用场景2. 目前问题3. 前期准备 具体步骤1. 拉取镜像2. 运行镜像3. 安装插件4. 代码支持4.1 config文件4.2 消费监听4.2 消息生产 5. 功能测试 镜像操作1. 镜像制作2. 镜像导入 总结 整体描述 1. 使用…

2023年中国光伏行业研究报告

第一章 行业概况 1.1 定义 光伏行业&#xff0c;也称为太阳能光伏行业&#xff0c;是一个专注于利用光伏技术将太阳能转化为电能的领域。该行业涵盖了太阳能电池的制造、光伏系统的设计、安装和维护&#xff0c;以及电能的销售和供应。光伏技术的核心是光伏效应&#xff0c;通…

java设计模式之观察者模式

. 基本概念 观察者&#xff08;Observer&#xff09;模式中包含两种对象&#xff0c;分别是目标对象和观察者对象。在目标对象和观察者对象间存在着一种一对多的对应关系&#xff0c;当这个目标对象的状态发生变化时&#xff0c;所有依赖于它的观察者对象都会得到通知并执行它…

图片怎么压缩大小?这样压缩图片很简单

在日常生活中&#xff0c;我们常常需要处理各种各样的图片文件&#xff0c;但有时候图片的大小可能会成为问题。比如在上传图片到网站或者将图片发送给朋友时&#xff0c;过大的图片可能会导致上传速度变慢或者占用过多内存。这时&#xff0c;我们就需要用到图片压缩了&#xf…

python 语法入门

文章目录 前言python 语法入门1. 语句分隔符2. 注释3. pep8规范4. 变量5. 扩展5.1. 运行此行代码的过程 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会…

SegGPT: Segmenting Everything In Context论文笔记

论文https://arxiv.org/pdf/2304.03284.pdfCodehttps://github.com/baaivision/Painter 文章目录 1. 背景2. Motivation3. Method3.1 In-Context Coloring3.2 Context Ensemble3.3 In-Context Tuning 1. 背景 在Painter中&#xff0c;将各种密集预测任务视为一种着色问题。 在…

类和对象(1)

文章目录 1.面向过程和面向对象初步认识2.类的引入3.类的定义4.类的访问限定符和封装4.1访问限定符4.2封装 5.类的作用域6.类的实例化6.2结构体内存对齐规则 7.this指针7.2this指针的特性 封装&#xff08;补充&#xff09; 1.面向过程和面向对象初步认识 C面向对象但不纯面向…

【Python】爬虫基础

爬虫是一种模拟浏览器实现&#xff0c;用以抓取网站信息的程序或者脚本。常见的爬虫有三大类&#xff1a; 通用式爬虫&#xff1a;通用式爬虫用以爬取一整个网页的信息。 聚焦式爬虫&#xff1a;聚焦式爬虫可以在通用式爬虫爬取到的一整个网页的信息基础上只选取一部分所需的…

关于运行franka_ros包中的franka_gazebo报错VMware: vmw_ioctl_command error 无效的参数.

参考的博文&#xff0c;感谢&#xff0c;解决Vmware下虚拟机下打开gazebo报错 &#xff0c;VMware: vmw_ioctl_command error 无效的参数. 首先第一个VMware: vmw_ioctl_command error 无效的参数的问题。这应该是虚拟机的bug&#xff0c;毕竟使用虚拟机和真实的物理机上是有差…

Error from server (NotFound): pods “nginx-57d84f57dc-b866m“ not found

原因&#xff1a;机房断电&#xff0c;导致服务重启 [rootmaster1 logs]# kubectl get pod NAME READY STATUS RESTARTS AGE nginx-57d84f57dc-57fkf 1/1 Running 0 75s [rootmaster1 logs]# kubectl logs -f nginx-5…

CPU-主存储器-副存储器-RAM-ROM-内存-运存-外存-硬盘-闪存-GPU-显存——关于这一系列概念的理解

文章目录 概念梳理CPU主存储器/内存/RAM/运存ROM副存储器/外存硬盘&#xff08;电脑&#xff09;、闪存&#xff08;手机&#xff09;GPU显存 可参考的手机内部结构示意图 做计算机组成原理上的题的时候&#xff0c;发觉自己对RAM和ROM的概念理解有所缺失&#xff0c;在看完一些…

QT Pyside2 Designer 的基本使用

文章目录 前言PySide2PySide2 Designer 一、安装PySide2、PyQt5二、使用designer.exe2.1 工具的大致介绍2.2 创建一个新的UI2.3 UI文件另存为/保存(CtrlS)2.4 使用python操作UI文件 总结 前言 PySide2 QT PySide2 是一个用于 Python 编程语言的开源框架&#xff0c;它提供了与…

Faster Rcnn

一、公用特征Feature Maps的获取 二、Region Proposal Network Feature Maps[bs,1024,38,38]经过3*3卷积&#xff0c;然后分别经过两个1*1的卷积&#xff0c;通道数分别为18&#xff0c;36 18 9*2 代表每个位置9个先验框为背景和目标的概率 36 9*4 代表每个位置9个先验框的…

pinduoduo(商品详情)API接口

为了进行电商平台 的API开发&#xff0c;首先我们需要做下面几件事情。 1&#xff09;开发者注册一个账号 2&#xff09;然后为每个pinduoduo应用注册一个应用程序键&#xff08;App Key) 。 3&#xff09;下载pinduoduo API的SDK并掌握基本的API基础知识和调用 4&#xff…

【Terraform】Terraform自动创建云服务器脚本

Terraform 是由 HashiCorp 创建的开源“基础架构即代码”工具 &#xff08;IaC&#xff09; 使用HCL&#xff08;配置语言&#xff09;描述云平台基础设施&#xff08;这里教你使用低级基础设施&#xff1a;交换机、云服务器、VPC、带宽&#xff09; Terraform提供者&#xf…

数据结构和算法(5):二叉树

树 向量允许通过下标或秩&#xff0c;在常数的时间内找到目标对象&#xff1b;然而&#xff0c;一旦需要对这类结构进行修改&#xff0c;那么无论是插入还是删除&#xff0c;都需要耗费线性的时间。 列表允许借助引用或位置对象&#xff0c;在常数的时间内插入或删除元素&…

Springboot整合JWT完成验证登录

目录 一、引入依赖二、JwtUtil 代码解读三、LoginController 代码解读四、整体代码五、结果展示 一、引入依赖 <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></depende…