右值引用

2023-11-12

一、右值引用简介

C++11增加了一个新的类型,称为右值引用(R-value reference),标记为 &&。在介绍右值引用类型之前要先了解什么是左值和右值:

  • lvalue是locator value的缩写,rvalue是 read value的缩写。
  • 左值是指存储在内存中、有明确存储地址(可取地址)的数据。
  • 右值是指可以提供数据值的数据(不可取地址)。

通过描述可以看出,区分左值与右值的便捷方法是:可以对表达式取地址(&)就是左值,否则为右值。
所有有名字的变量或者对象都是左值,而右值是匿名的。

int a = 520;
int b = 1314;
a = b;

一般情况下,位于 = 前面的表达式为左值,位于 = 后边的表达式为右值。也就是说例子中的a,b为左值,520,1314为右值。a=b是一种特殊情况,在这个表达式中a,b都是左值,因为变量b是可以被取地址的,不能视为右值。

int value = 520;

上面语句中的value为左值,520为字面量也就是右值。其中value可以被引用,但是520就不行了,因为字面量都是右值。

二、右值引用的使用

右值引用就是对一个右值进行引用的类型。因为右值是匿名的,所以我们只能通过引用它的方式找到它。无论声明左值引用还是右值引用都必须立即进行初始化,因为引用类型本身并不拥有所绑定对象的内存,只是该对象的一个别名。通过右值引用的声明,该右值又”重获新生”。

其生命周期与右值引用类型变量的声明周期一样,只要该变量还活着,该右值临时量将会一直存活下去。

关于右值引用的使用,代码如下:

#include <iostream>
using namespace std;

int&& value = 520;
class Test
{
public:
    Test()
    {
        cout << "construct: my name is jerry" << endl;
    }
    Test(const Test& a)
    {
        cout << "copy construct: my name is tom" << endl;
    }
};

Test getObj()
{
    return Test();
}

int main()
{
    int a1;
    //int&& a2 = a1;        // error
    //Test& t = getObj();   // error
    Test&& t = getObj();
    const Test& t1 = getObj();
    return 0;
}
  • 在上面的例子中int &&value=520;里面的520为纯右值,value是字面量520这个右值的引用。
  • 在int &&a2=a1中;a1虽然写在=的右边,但是它仍然是一个左值,使用左值初始化一个右值引用是不合法的。
  • Test & t=getObj()这段代码语法是错误的,右值不能给普通的左值引用赋值。
  • Test && t=getObj()中getObj()返回的临时对象称为将亡值,t是这个将亡值的右值引用。
  • const Test & t1=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()
    {
        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;
};

测试代码的输出结果:
在这里插入图片描述

通过输出结果我们可以看到调用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;
};

测试代码输出的结果:
在这里插入图片描述

通过修改,在上面的代码给Test类添加了移动构造函数(参数为右值引用类型),这样在进行Test t=getObj()操作时候并没有调用拷贝构造函数进行深拷贝,而是调用移动构造函数,这个函数中只是进行了浅拷贝,没有对临时对象进行深拷贝,提高性能。
如果不使用移动构造,在执行Test t=getObj()时候也是进行浅拷贝,但是临时对象被析构的时候,类成员指针int *m_num指向的内存也就被析构了,对象t也就无法访问这块内存地址了。
在测试程序中getObj()的返回值就是一个将亡值,也就是一个右值,在进行赋值操作的时候如果右边是一个右值,那么移动构造就会被调用。移动构造中使用了右值引用,会将临时对象的堆内存地址的所有权转移给对象t,这块内存被成功续命,因此在t对象中还可以继续使用这块内存。

注意:对于需要动态申请大量资源的类,应该设计移动构造函数,以提高程序效率。需要注意的是,我们一般在提供移动构造函数的同时,也会提供常量左值引用的拷贝构造函数,以保证移动不成还可以使用拷贝构造函数。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

右值引用 的相关文章

  • 部署 MVC4 项目时出错:找不到文件或程序集

    过去 我只需使用 Visual Studio 2012 发布到 AWS 菜单项即可部署我的 MVC4 网站 到 AWS Elastic Beanstalk 现在 程序可以在本地编译并运行 但无法部署 从消息来看 它似乎正在寻找不在当前部署的
  • 在 LINQ 查询中返回不带时间的日期

    我正在编写一个查询 我想计算按日期联系我们的呼叫中心的次数 看起来很简单 但由于联系日期字段是日期时间字段 我得到了时间 因此当我按联系日期 时间 分组时 每个联系日期实例的计数为 1 所以 我想只按日期分组 而不按时间分组 下面是我用来查
  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • 在 Xamarin Android 中将图像从 URL 异步加载到 ImageView 中

    我有一个包含多个项目的 ListView 列表中的每个项目都应该有一个与之关联的图像 我创建了一个数组适配器来保存每个列表项并具有我希望加载的图像的 url 我正在尝试使用 Web 请求异步加载图像 并设置图像并在加载后在视图中更新它 但视
  • 如何在C++中实现模板类协变?

    是否可以以这样一种方式实现类模板 如果模板参数相关 一个对象可以转换为另一个对象 这是一个展示这个想法的例子 当然它不会编译 struct Base struct Derived Base template
  • 嵌入式系统中的malloc [重复]

    这个问题在这里已经有答案了 我正在使用嵌入式系统 该应用程序在 AT91SAMxxxx 和 cortex m3 lpc17xxx 上运行 我正在研究动态内存分配 因为它会极大地改变应用程序的外观 并给我更多的力量 我认为我唯一真正的路线是为
  • 使用 Microsoft Graph API 订阅 Outlook 推送通知时出现 400 错误请求错误

    我正在尝试使用 Microsoft Graph API 创建订阅以通过推送通知获取 Outlook 电子邮件 mentions 我在用本文档 https learn microsoft com en us graph api subscri
  • 跨多个控件共享事件处理程序

    在我用 C 编写的 Windows 窗体应用程序中 我有一堆按钮 当用户的鼠标悬停在按钮上时 我希望按钮的边框发生变化 目前我有以下多个实例 每个按钮一个副本 private void btnStopServer MouseEnter ob
  • HttpClient 像浏览器一样请求

    当我通过 HttpClient 类调用网站 www livescore com 时 我总是收到错误 500 可能服务器阻止了来自 HttpClient 的请求 1 还有其他方法可以从网页获取html吗 2 如何设置标题来获取html内容 当
  • 当 Cortex-M3 出现硬故障时如何保留堆栈跟踪?

    使用以下设置 基于 Cortex M3 的 C gcc arm 交叉工具链 https launchpad net gcc arm embedded 使用 C 和 C FreeRtos 7 5 3 日食月神 Segger Jlink 与 J
  • 更改窗口的内容 (WPF)

    我创建了一个简单的 WPF 应用程序 它有两个 Windows 用户在第一个窗口中填写一些信息 然后单击 确定 这会将他们带到第二个窗口 这工作正常 但我试图将两个窗口合并到一个窗口中 这样只是内容发生了变化 我设法找到了这个更改窗口内容时
  • 网络参考共享类

    我用 Java 编写了一些 SOAP Web 服务 在 JBoss 5 1 上运行 其中两个共享一个类 AddressTO Web 服务在我的 ApplycationServer 上正确部署 一切都很顺利 直到我尝试在我的 C 客户端中使用
  • C 中的位移位

    如果与有符号整数对应的位模式右移 则 1 vacant bit will be filled by the sign bit 2 vacant bit will be filled by 0 3 The outcome is impleme
  • 可空属性与可空局部变量

    我对以下行为感到困惑Nullable types class TestClass public int value 0 TestClass test new TestClass Now Nullable GetUnderlyingType
  • 作为字符串的动态属性名称

    使用 DocumentDB 创建新文档时 我想设置属性名称动态地 目前我设置SomeProperty 像这样 await client CreateDocumentAsync dbs db colls x new SomeProperty
  • 已过时 - OpenCV 的错误模式

    我正在使用 OpenCV 1 进行一些图像处理 并且对 cvSetErrMode 函数 它是 CxCore 的一部分 感到困惑 OpenCV 具有三种错误模式 叶 调用错误处理程序后 程序终止 Parent 程序没有终止 但错误处理程序被调
  • ListDictionary 类是否有通用替代方案?

    我正在查看一些示例代码 其中他们使用了ListDictionary对象来存储少量数据 大约 5 10 个对象左右 但这个数字可能会随着时间的推移而改变 我使用此类的唯一问题是 与我所做的其他所有事情不同 它不是通用的 这意味着 如果我在这里
  • 在Linux中使用C/C++获取机器序列号和CPU ID

    在Linux系统中如何获取机器序列号和CPU ID 示例代码受到高度赞赏 Here http lxr linux no linux v2 6 39 arch x86 include asm processor h L173Linux 内核似
  • 方法参数内的变量赋值

    我刚刚发现 通过发现错误 你可以这样做 string s 3 int i int TryParse s hello out i returns false 使用赋值的返回值是否合法 Obviously i is but is this th
  • 更改显示的 DPI 缩放大小使 Qt 应用程序的字体大小渲染得更大

    我使用 Qt 创建了一些 GUI 应用程序 我的 GUI 应用程序包含按钮和单选按钮等控件 当我运行应用程序时 按钮内的按钮和字体看起来正常 当我将显示器的 DPI 缩放大小从 100 更改为 150 或 200 时 无论分辨率如何 控件的

随机推荐

  • js逆向系列:企名片,获取js逆向后的真实数据!

    一 进入企名片创业项目 我们需要爬取如下数据 首先 对该网页进行抓包 发现这些数据是通过post请求获得的 这是网站给我们返回的数据 为什么和网页上显示的不一样呢 分析后得出 这是经过js加密后的数据 为了防止爬虫 网页对数据进行了加密 因
  • 没有50W彩礼,该怎么办

    大家好 我是才哥 刚过完春节 作为到了已婚甚至被催婚年龄的我们也开始讨论一个自古既有的话题 彩礼 今天上午 看到朋友圈刷屏了一个B站UP主的视频 没有50W彩礼 女朋友被强行拖走 我该怎么办 看完视频只想说 https www bilibi
  • Android面经_安卓面经(25/30)之MVC、MVP、MVVM全解析

    系列专栏 安卓高频面经解析大全专栏链接 150道安卓高频面试题全解析 安卓高频面经解析大全目录详情 安卓面经 anroid面经 150道安卓常见基础面试题全解析 安卓系统Framework面经专栏 Android系统Framework面试题
  • Python 5大常用魔术方法汇总

    前言 Python 中 以双下划线 包起来的方法 统称为 魔术方法 Magic Method 魔术方法是一个类或对象中的特殊方法 和普通方法的区别在于 普通方法需要手动调用 而魔术方法是在特定时刻自动触发执行的 如果希望根据自己的程序定制自
  • 开放原子开源基金会秘书长孙文龙:要打造以开发者为本的开源服务平台

    7月28日 2022开放原子全球开源峰会在北京亦创国际会展中心隆重举行开幕式 开放原子开源基金会秘书长孙文龙发表题为 凝心聚力 共拓开源 的演讲 开源开放 应运而生 开放原子开源基金会于2020年6月正式成立 作为我国首家开源基金会 也是目
  • 第一个solidity程序

    一 示例程序 SPDX License Identifier GPL 3 0 pragma solidity gt 0 4 16 lt 0 9 0 contract SimpleStorage uint storedData functio
  • 487. 金明的预算方案

    Powered by NEFU AB IN Link 文章目录 487 金明的预算方案 题意 思路 代码 487 金明的预算方案 题意 略 思路 可以将每个主件及其附件看作一个物品组 记主件为 p 两个附件为 a b 则最多一共有4种组合
  • ftp的passive模式

    昨天调试了半天的ftp passive模式 记录一下 今天在一台测试服务器上搭建ftp 折腾了许久 主要是不了解ftp的passive模式和port模式的区别 这里记录一下 和passive模式对应的叫做port模式 也叫做standard
  • Vue进阶——Vue CLI

    Vue进阶 Vue CLI 前言 一 什么是Vue CLI 二 怎么安装Vue CLI 1 Node 版本要求 2 已安装旧版本 3 安装完成 三 Vue CLI 创建Vue项目 1 Vue ui项目创建 1 1 预设 1 2 项目功能 1
  • Realme的Login接入过程记录

    为什么80 的码农都做不了架构师 gt gt gt 1 问题的提出 近期要使用Realme作为第三方接入 因为研究了一下Realme的请求过程 Request 和响应 Response 其中包含了一些加密和解密的方式 我们接下来分别从Req
  • 【Qt教程】1.9 - Qt5菜单栏、工具栏、状态栏、核心窗口、浮动窗口、QMainWindow

    1 窗口应用布局样式及组成 PC端软件 最基本的一个窗口应用布局样式 如下 大体可分为菜单栏 工具栏 状态栏 核心窗口 浮动窗口 使用时按需相互组合 2 Qt中调用这些控件 1 新建一个工程 使用QMainWindow类 2 例程源码 具体
  • Spring Security跨域问题解决

    前文介绍了 Spring 处理跨域问题的三种方案 现在来看看 Spring Security 的跨域问题解决方案 共有三种方案 摘自 深入浅出Spring Security 在实际项目使用中 推荐使用第三种方案 11 3 3 专业解决方案
  • 压缩感知及应用 源代码_【DMD应用】基于压缩感知超分辨鬼成像

    概述 分辨率是成像系统的一个重要参数 获得高分辨率图像一直是鬼成像系统的一个目标 本文提出了以成 像系统点扩散函数作为先验知识 基于稀疏测量的超分辨压缩感知鬼成像重建模型 搭建了一套计算鬼成像 实验装置 用于验证该模型对于提高鬼成像系统分辨
  • sql server登录名、服务器角色、数据库用户、数据库角色、架构区别联系

    1 一个数据库用户可以对应多个架构 架构是表容器 架构里面包含的是数据库表 2 一个数据库角色有可能涉及多个架构 数据库角色对应的是权限 3 一个用户对应一个数据库角色 4 登录名与数据库用户在服务器级别是一对多的 在数据库级别是一对一的
  • Java语言写一个简单的学生信息管理系统,通过JDBC连接数据库对学生信息进行增删改查,采用三层思想和DBUtils第三方框架。

    我把源代码和sql文件放GitHub上了 你们可以自行下载 https github com fenglily1 student 有问题可以留言或私信 我看到就会回 进阶版加上页面的管理系统在这篇 https blog csdn net w
  • linux汇编编译器:GAS和NASM的比较

    GAS即GNU AS汇编编译器 其属于AT T风格 我们常用的GNU的产品还有GCC G NASM是Linux平台下常用的汇编编译器 是intel风格的汇编编译器 MASM是Windows平台下的汇编编译器 也使用Intel风格 我们学80
  • C语言之简单版本银行储蓄系统4(结构体版本)

    1 老学长的唠叨 在上一个编的数组版本的简化银行系统的基础上改为结构体存储 为还没有学到结构体的学弟学妹们提供一个迁就的方案 现在将程序改为结构体存储了 也希望学弟学妹们有个c语言学习缓冲的时间 这个代码没有用多文件组织的目的是方便代码的拷
  • STM32-CubeMx-HAL库-串口空闲中断+DMA——利用HAL_UARTEx_ReceiveToIdle_DMA实现不定长数据接收

    STM32 CubeMx HAL库 串口空闲中断 DMA 利用HAL UARTEx ReceiveToIdle DMA实现不定长数据接收 1 主要函数 2 CubeMX配置 2 1 在串口配置中添加DMA USART Rx 2 2 中断设置
  • CTP的交易指令类型

    在入场进行期货交易的时候 我们会向期货交易所放送订单 除了最基本的多空方向以及价格之外 订单还有不同的执行时机 触发条件 和执行方式 不清楚不同交易订单的特性 很容易造成本来可以避免的交易损失 我花点时间捋一遍CTP系统支持的不同种类的交易
  • 右值引用

    文章目录 一 右值引用简介 二 右值引用的使用 三 性能优化 一 右值引用简介 C 11增加了一个新的类型 称为右值引用 R value reference 标记为 在介绍右值引用类型之前要先了解什么是左值和右值 lvalue是locato