C++学习笔记----8、掌握类与对象(一)---- 对象中的动态内存分配(4)

2.4、使用Move语法处理移动

        类的move语法要求一个move构造函数和一个move赋值操作符。这些可以用于编译器,当源对象是一个临时对象在操作完成时就会被析构,你马上就会看到,显式地使用std::move()。move移动内存的属主以及从一个对象到另一个对象的其它资源。从根儿上说是做了一个shallow拷贝,拷贝与交换分配内存和其它资源以名悬浮指针或者资源并且防止内存渗露的属主相结合的数据成员。

        move构造函数与move赋值操作符移动数据成员从一个源对象到一个新的对象,将源对象置于一个有效但处于中间状态 。通常,源对象的数据成员会被置为“null”值,但这并不是一个严格的要求。推荐确保源对象处于一个清晰定义的空状态,在移动操作之后。安全起见,不要用任何移动的源对象,因为这可以会引发没有定义的行为。有些标准库的著名例外就是std::unique_ptr与shared_ptr。标准库显式地指出这些智能指针一定要重置它们的内部指针为nullptr,当从它们移动出,这使得在移动操作之后重用这些智能指针是安全的。

        在实现move语法之前,需要学习一下右值与右值引用。

2.4.1、右值引用

        在c++中,左值是可以占用一个地址的东东,例如,一个命名的变量。名字来自于左值可以出现在赋值的左手边的事实。另一方面,右值,它除了不是一个左值,可以是例如常量,或者临时对象或者值的任何东东。典型地,一个右值就是在赋值操作符的右手边。例如,考虑下面的语句:

int a { 4 * 2 };

        在这个语句中,a是一个左值,它有名字,可以通过&a来得到它的地址。结果是表达式4*2,另一方面,它就是一个右值。它是一个临时值,在语句执行完成后就会被破坏掉。在这个例子中,临时值的一个拷贝被保存在名字叫做a的变量中。

        如果函数返回一个值,调用该函数的结果就是一个右值,一个临时值。如果函数返回一个reference-to-non-const,调用该函数的结果就是一个左值,因为你可以使用该结果在赋值的左手边。

        右值引用是对右值的一个引用。特别地,它是一个在右值是一个临时对象或者一个显式移动使用std::move()的对象的应用概念。右值引用的目的是使得当右值介入的时候可以选择一个特殊的函数重载成为可能。这允许正常的拷贝大的值的操作而不是拷贝指向这些值的指针。

        函数可以通过使用&&作为参数指标的一部分来指定一个右值引用参数。正常情况下,一个临时对象可以看作是一个const type&,但是当有一个使用右值引用的函数重载时,一个临时对象可以被解释成那个重载。下面举例说明。代码首先定义了两个handleMessage()函数,一个接收左值引用,一个接收右值引用:

void handleMessage(string& message) // lvalue reference parameter
{println("handleMessage with lvalue reference: {}", message);
}void handleMessage(string&& message) // rvalue reference parameter
{println("handleMessage with rvalue reference: {}", message);
}

        可以用一命名变量作为参数调用handleMessage():

string a { "Hello " };
handleMessage(a);
// Calls handleMessage(string& value)

        因为a是一个命名变量,接收左值引用的handleMessage()函数被调用。通过引用参数handleMessage()所做的任何改变都会改变a的值。

        也可以用一个表达式作为参数来调用handleMessage():

string b { "World" };
handleMessage(a + b);
// Calls handleMessage(string&& value)

        接收左值引用的handleMessage()无法使用,因为表达式a + b的结果是一个临时值,它不是一个左值。在这种情况下,右值引用重载被调用。因为参数是一个临时的,在调用返回后,任何通过引用参数handleMessage()所做的改变都会丢失。

        常量也可以用于handleMessage()的参数。它也会触发对于右值引用重载的调用,因为一个常量不可能为左值(虽然常量可以作为参数传递给reference-to-const参数):

handleMessage("Hello World"); // Calls handleMessage(string&& value)

        如果移除掉接收左值引用的handleMessage()函数,用一个命名变量调用handleMessage(),比如handleMessage(b)就会出现编译错误,因为右值引用参数(string&&)不会连接上左值(b)。可以通过使用std::move()来强制编译器调用右值引用重载的handleMessage()。move()所做的唯一一件事就是将左值引用转化为右值引用;也就是说,它没有进行任何实质上的移动。然而,通过返回一个右值引用,它允许编译器找到接收右值引用的handleMessage()的重载,它就可以执行移动了。下面是一个使用move()的例子:

handleMessage(std::move(b));
// Calls handleMessage(string&& value)

        重要的事情需要多说,命名变量是一个左值。所以,在handleMessage(string&& message)函数中,message右值引用参数自身是一个左值,因为它有一个名字!如果你想传递这个右值引用参数给另一个函数作为右值,就需要使用std::move()来将这个左值转化成右值引用了。例如,假定你添加了如下的带有右值引用参数的函数:

void helper(string&& message) { }

        像下面这样进行调用是编译不成功的:

void handleMessage(string&& message) { helper(message); }

        helper()函数需要一个右值引用,而handleMessage()传递了message,它有一个名字,所以它是一个左值,导致一个编译错误。下面是使用std::move()的正确方式:

void handleMessage(string&& message) { helper(std::move(message)); }

        警告:命名右值引用,比如一个右值引用参数,自身是一个左值,因为它有一个名字!

        右值引用不仅仅限于函数参数。可以声明一个右值引用类型的变量并给其赋值,虽然这种用法不常见。考虑下面的代码,在c++中是不合法的:

int& i { 2 };    // Invalid: reference to a constant
int a { 2 }, b { 3 };
int& j { a + b };    // Invalid: reference to a temporary

        而使用右值引用,下面就是完美的合法:

int&& i { 2 };
int a { 2 }, b { 3 };
int&& j { a + b };

        然而,这样单独的右值引用很少以这种方式使用,也没有什么卵用!

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

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

相关文章

FortiGate 无线组网

无线管理与配置 FortiAP 连接 internal 接口之后自动获得 ip 地址:192.168.1.xxx/24在 FortiGate 中创建 SSIDFortiGate 自动发现 FortiAP,将 FortiAP 添加到 FortiGate将 SSID 和 FortiAP 关联创建防火墙策略 下面我们就来一起看看在 FortiGate 中该如…

ModbusTCP通讯错误的排查

Modbus是一种由MODICON公司开发的工业现场总线协议标准,是一项应用层报文传输协议。该协议用于传输数字和模拟变量[1]。有关该协议的报文具体格式,以及一些基本概念,见[1]。 本文以一个例子,阐述当ModbusTCP通讯出现错误的时候&a…

开源鸿蒙OpenHarmony系统更换开机Logo方法,瑞芯微RK3566鸿蒙开发板

本文适用于开源鸿蒙OpenHarmony系统更换开机Logo,本次使用的是触觉智能的Purple Pi OH鸿蒙开源主板,搭载了瑞芯微RK3566芯片,类树莓派设计,是Laval官方社区主荐的一款鸿蒙开发主板。 介绍 OpenHarmony的品牌标志、版本信息、项目…

计算机毕业设计 基于Hadoop的智慧校园数据共享平台的设计与实现 Python 数据分析 可视化大屏 附源码 文档

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…

探索EasyCVR视频融合平台:在视频编解码与转码领域的灵活性优势

随着视频监控技术的飞速发展,各类应用场景对视频数据的处理需求日益复杂多样。从公共安全到智慧城市,再到工业监控,高效、灵活的视频处理能力成为衡量视频融合平台性能的重要标准。在众多解决方案中,EasyCVR视频融合平台凭借其在视…

大觅网之自动化部署(Automated Deployment of Da Mi Network)

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…

音视频整体解码流程和同步流程

目录 1. 整体解码流程1. 初始化 FFmpeg2. 打开媒体文件3. 查找解码器4. 打开解码器5. 读取和解码数据6. 处理解码后的帧7. 释放资源 2. 音视频同步整体流程1. 解复用媒体流2. 解码3. 以音频为时钟源进行音视频同步的策略4. 缓冲区设计 现在先说大体流程,不分析代码 …

使用python爬取豆瓣网站?如何简单的爬取豆瓣网站?

1.对python爬虫的看法 首先说说我对python的看法,我的专业是大数据,我从事的工作是java开发,但是在工作之余,我对python又很感兴趣,因为我觉得python是一门很好的语言,第一:它可以用来爬取数据…

如何使用 Rust 框架进行 RESTful API 的开发?

一、RESTful API 的开发 使用 Rust 框架进行 RESTful API 开发,你可以选择多种流行的 Rust Web 框架,如 Actix-web、Rocket、Warp 和 Tide 等。以下是使用这些框架进行 RESTful API 开发的基本步骤和概念: 选择框架:根据项…

探索 Snowflake 与 Databend 的云原生数仓技术与应用实践 | Data Infra NO.21 回顾

上周六,第二十一期「Data Infra 研究社」在线上与大家相见。活动邀请到了西门子数据分析师陈砚林与 Databend 联合创始人王吟,为我们带来了一场关于 Snowflake 和 Databend 的技术探索。Snowflake,这个市值曾超过 700 亿美元的云原生数据仓库…

李宏毅机器学习2023-HW10-Adversarial Attack

文章目录 TaskBaselineFGSM (Fast Gradient Sign Method (FGSM)I-FGSM(Iterative Fast Gradient Sign Method)MI-FGSM(Momentum Iterative Fast Gradient Sign Method)M-DI2-FGSM(Diverse Input Momentum Iterative Fast Gradient Sign Method) Reportfgsm attackJepg Compress…

性能优化与资源管理:优化Selenium脚本的执行效率,合理管理浏览器实例和系统资源

目录 引言 一、Selenium基础与常用方法 1.1 Selenium简介 1.2 Selenium基础用法 二、Selenium性能优化技巧 2.1 使用WebDriverWait实现显式等待 2.2 启用无头模式 2.3 设置合理的页面加载策略 2.4 禁用图片和JavaScript加载 2.5 优化元素定位 2.6 合理使用隐式等待和…

从0开始linux(5)——vim

欢迎来到博主的专栏:从0开始linux 博主ID:代码小豪 文章目录 vim的多种模式底行模式命令命令模式视块模式(visual block) vim的配置 vim是linux系统的文本编辑器。就像windows的记事本一样。 使用vim指令可以使用vim打开一个文本文…

JavaWeb美食推荐管理系统

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 spring-mybatis.xml3.5 spring-mvc.xml3.5 login.jsp 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍:CSDN认证博客专家,CSDN平台Java领域优…

JavaScript 学习

一、输出 为方便调试可以输出内容&#xff0c;但是用户是看不到的。要在开发者模式中看。 console . log ( "Hello" )&#xff1b; 二、外部文件引用 可以直接在html中写JS <head> <meta charset"utf-8"> <script> console.log("he…

ZYNQ:开发环境搭建

资料下载 http://47.111.11.73/docs/boards/fpga/zdyz_qimxing(V2).html Vivado软件是什么&#xff1f; Vivado软件是Xilinx&#xff08;赛灵思&#xff09;公司推出的一款集成设计环境&#xff08;IDE&#xff09;&#xff0c;主要用于FPGA&#xff08;现场可编程门阵列&am…

零代码构建自己强大的Agent智能体,偷偷甩掉90%的人

转自公众号&#xff1a;渡码 Agent&#xff08;智能体&#xff09;的概念大家应该并不陌生了&#xff0c;今天分享通过可视化的方式构建各种各样强大的智能体。 关于Agent的定义&#xff0c;我并不想引用官方正式的说法。而是按照我的理解通俗地解释一下。 大模型好比是面粉…

第166天:应急响应-拒绝服务钓鱼指南DDOS压力测试邮件反制分析应用日志

案例一&#xff1a;内网应急-日志分析-爆破&横向&数据库 数据库 这里不同数据库日志不一样&#xff0c;我用mysql分析 首先MySQL数据库需要支持远程连接 GRANT ALL PRIVILEGES ON . TO root% IDENTIFIED BY 123.com WITH GRANT OPTION; 其次开启日志 -- 查看general…

Flutter为Android添加签名并打包

前言 我们需要将App进行数字签名才能发布到商店里。在这里就具体描述一下如果给App添加签名 为App签名 创建一个用户上传的秘钥库 如果你已经有一个秘钥库了&#xff0c;可以直接跳到下一步&#xff0c;如果没有则按照下面的指令创建一个 keytool 可能不在我们的系统路径中…

MySQL多版本并发控制MVCC实现原理

MVCC MVCC 是多版本并发控制方法&#xff0c;用来解决读和写之间的冲突&#xff0c;比如脏读、不可重复读问题&#xff0c;MVCC主要针对读操作做限制&#xff0c;保证每次读取到的数据都是本次读取之前的已经提交事务所修改的。 概述 当一个事务要对数据库中的数据进行selec…