条款29:假定移动操作不存在、成本高、未使用

news2024/11/26 0:25:06

移动语义可以说在C++11的所有语言特性中占据着首要中的首要地位。“移动容器现在和复制指针一样成本低廉了!”这是你很可能听说过的,类似说法还有“复制临时对象现在已经如此高效,如果刻意在撰写代码总避免它,就无异于犯了过早优化的禁忌!”这些情绪化的言辞不难理解。移动语义确实是个重要的语言特性。语言不只是允许编译器使用成本相对低廉的移动操作来代替昂贵的复制操作,实际上语言会要求编译器这样做(只要满足适当条件就必须这样做)。调出你的C++98版本的代码存根,然后只消一字不改地使用符合C++11标准的编译器和标准库重新编译一遍,叫一声“变!”,你的软件便应声增速。

移动语义确有此功,所以这个语言特性一传十,十传百,渐渐成了传奇,传递一般都是夸大其词的结果。本条款就是想让你对这个语言特性的期望接上地气。

让我们从为何许多型别不能支持移动语义的观察开始。整个C++98标准库都已为C++11彻底翻修过。目的就是为那些的型别移动的可以实现成比复制更快的型别增添移动操作,而且库组织的实现也已完成修订以充分利用这些移动操作,不过问题在于你有可能手上的代码存根并未完成修订以充分利用C++11的良好特性。若你的应用中的(或采用的库中的)型别没有为C++11做过专门修改,那么仅仅在编译器中有着对移动操作的支持也并不会给你带来什么明显的好处。诚然,C++11愿意为这些缺少移动操作的类生成移动操作,但这仅适用于那些未声明复制操作、移动操作以及析构函数的类(参见条款17)。型别若是有数据成员或是基类禁用了移动(例如,通过将移动操作删除的方式参见条款11),也将导致编译器生成的移动操作被抑制掉。如果型别并不提供对移动的显式支持,也不符合编译器生成的移动操作的条件,当然也就没有理由期望它在C++11下比在C++98下有任何性能的提升了。

即使对于那些显式支持移动操作的型别,也可能不会像人们希望的那样带来那么大的利好。举个例子,在标准的c++11库中,所有的容器都支持移动操作,但如果因此就断言所有的容器的移动都是成本低廉的,那就贻笑大方了。对于有些容器而言,它们虽然有着确实成本低廉的移动操作,但却又有一些附加条件造成其容器元素不能满足。

考虑一下std::array这个C++11中引入的新容器型别,它实质上就是带有STL接口的内建数组。这一点和其他标准库容器存在根本差异,因为其他标准容器都是将其内容存放在堆上的,从而在概念上,只需(以数据成员的方式)持有一个指涉到存放容器内容的堆内存的地址(实际情况当然比这个更复杂些,但出于这里的分析目的,这些差异并不要紧)。由于该指针的存在,把整个容器的内容在常数时间内日加以移动就成为了可能:仅仅把那个指针到容器内容的指针从源容器复制到目标容器,尔后吧源容器包含的指针置空即可:

std::vector<Widget> vw1;
//将数据放入vw1
...

//移动vw1入vw2
//完成执行仅需常数时间
//仅仅是包含在vw1和vw2中的指针被修改了
auto vw2 = std::move(vw1);

 而std::array型别的对象则缺少这样一个指针,因为其内容数据是直接存储在对象内的。

std::array<Widget, 10000> aw1;
//将数据放入aw1

...

//移动aw1入aw2
//完成执行需要线性时间
//需要把aw1中的所有元素移动入aw2
auto aw2 = std::move(aw1);

 请注意,aw1中的元素是被移动入aw2中的,假定Widget型别的移动比复制更快,则移动一个元素型别为Widget的std::array自然也会比复制一个同样的std::array更快。毫无疑问,std::array当然提供移动操作。然而无论是移动还是复制std::array型别的对象都还是需要线性时间的计算复杂度,因为容器中的每个元素都必须逐一复制或移动。这可比人们有时会听说的“移动容器现在和赋值一堆指针一样成本低廉了”这类说法,要超出太多了。

作为对比,std::string型别提供的是常数时间的移动和线性时间的复制。这听起来像是在说,它的移动比复制更快,但可能并非如此。许多string的实现都采用了小型字符串优化(SSO),采用了SSO以后,“小型”字符串(例如,容量不超过15个字符的字符串)会存储在std::string对象内的某个缓冲区内,而不去使用在堆上分配的存储。在使用了基于SSO的实现的前提下,对小型字符串实施移动并不比复制更快,因为通常在移动的性能优于复制背后“仅复制一个指针”的把戏会失灵了。

有着发明SSO的动机,就充分表明短字符串才是许多应用程序的标配,使用一个内部缓冲区来存储这样的字符串,就消弭了为它们动态分配内存的需要,而这往往在效率上是一步胜手,而这样做是一步胜手,就隐含着移动并不比复制更快的意思,当然正好比半杯水既可以说是半空也可以说半满,前面这句话同样可以说成复制并不比移动更慢。

即使是那些支持快速移动操作的型别,一些看似万无一失的移动场景还是以复制副本告终,条款14解释过,标准库中一些容器操作提供了强异常安全保证,并且为了确保依赖于这样的保证,并且为了确保依赖于这样的保证的那些C++98的遗留代码在升级到C++11时不会破坏这样的保证,底层的复制操作只有在已知移动操作不会抛出异常的前提下才会使用移动操作将其替换。这么做导致一个后果是,即使某个型别的移动操作比对应的复制操作更高效,甚至在代码的某个特定位置,移动操作一般肯定不会有问题(例如,源对象是个右值的情况下),编译器仍会强制去调用一个复制操作,只要对应的移动操作未加上noexcept声明。

总而言之,在这样几个场景中,C++11的移动语义不会给你带来什么好处:

  • 没有移动操作:待移动的对象未能提供移动操作。因此,移动请求变成了复制请求。
  • 移动未能更快:待移动的对象虽然有移动操作,但并不比其复制操作更快。
  • 移动不可用:移动本可以发生的语境下,要求移动操作不可发射异常,但该操作未加上noexcept声明。

值得一提的是,还有另一种场景使得移动语义无法提供效率增益:

源对象是个左值:除了极少数例外(参见条款25中的例子),只有右值可以作为移动操作的源。

但本条款的标题是假定移动操作不存在、成本高、未使用。这比较典型地适合于通用代码的情形,例如,撰写模板的时候,因为你还不知道将要与哪些型别配合。在这种情况下,你必须像在使用C++98一样保守地去复制对象,正如移动语义上不存在那样。这同样也符合“不稳定”代码的情形,即代码中所涉及型别的特征会比较频繁地加以修改。

然而,通常你已知代码中会使用的型别,也可以肯定它们的特征不会改变(例如,它们是否支持成本低廉的移动操作)。如果是这样的情况,你就不需要前面那些假定。你可以直接查阅所使用的型别对移动的支持细节。如果涉及的型别能够提供成本低廉的移动操作,并且是在这些移动操作会被调用的语境中使用对象,则可以放心大胆地依靠移动语义来将复制操作替换成相对不那么昂贵的对应移动操作。

要点速记

  • 假定移动操作不存在、成本高、未使用。
  • 对于那些型别或对于移动语义的支持情况已知的代码,则无需作以上假定。

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

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

相关文章

java SSM 程序在线评判系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM 程序在线评判系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采…

PDCA循环

PDCA循环 由美国质量管理专家沃特阿曼德休哈特&#xff08;Walter A. Shewhart&#xff09;首先提出的&#xff0c;由戴明采纳、宣传&#xff0c;获得普及&#xff0c;所以又称戴明环。 模型介绍 戴明是一位美国的质量管理大师&#xff0c;却成名于日本。在他的帮助下&#xf…

JQuery 操作Class实现前段交互方案(推荐)

一、JQuery基础控制图片宽度实现动画交互 1.html页面声明周期 //页面生命周期 //页面的数据html&#xff0c;加载完成&#xff0c; 图片ajax视频 在异步加载中 //document.ready---DOMContentLoaded ----小程序onload ---Vue created() //页面加载完成 //window.onload…

二叉树的相关操作

一.二叉树 本文的数据结构基于C语言练习。 C语言中的二叉树是一种数据结构&#xff0c;用于表示具有层次关系的数据集合。它由一个根节点开始&#xff0c;每个节点最多有两个子节点&#xff0c;分别称为左子节点和右子节点。 二叉树有许多相关性质&#xff0c;其中一些重要的包…

记录一下idea黄色警戒线问题

记录一下idea黄色警戒线问题 一、通用文件中解决黄色波浪线问题1.选中File中的Settings进入2.点击Editor&#xff0c;选中Inspections&#xff0c;找到General&#xff0c;找到Dulicated code fragment点击取消 二、SQL文件中黄色警告线 一、通用文件中解决黄色波浪线问题 1.选…

jvm之7种垃圾回收器解读(下)

目录 G1回收器&#xff1a;区域化分代式 G1回收器的特点&#xff08;优势&#xff09; 空间整合 可预测的停顿时间模型&#xff08;即&#xff1a;软实时soft real-time&#xff09; G1垃圾收集器的缺点 G1回收器的参数设置 G1收集器的常见操作步骤 G1收集器的适用场景 分…

Spring SpringMVC Mybatis 整合 SSM整合 一篇就够了!

SSM详细整合教程 因为XML注解方式实现更加方便&#xff0c;所以我门选用它 文章目录 SSM详细整合教程一、整合思路二、整合步骤0. 前期准备引入Jar包与Web目录创建1. Spring框架编写1.1 创建Application配置文件 2. SpringMvc框架编写2.1 创建Springmvc-config文件2. 2 配置前端…

用栈模拟实现队列(c语言版)

前言 用"栈实现队列",力扣中一道oj题,可以帮助刚接触"栈"和"队列"的新手更好的理解栈和队列这两种结构. 题目来源于力扣: 题目链接:https://leetcode.cn/problems/implement-queue-using-stacks/ 难度:简单 目录 前言一、队列的各接口:1.1 类型…

机器学习实战六步法之数据收集方法(四)

要落地一个机器学习的项目&#xff0c;是有章可循的&#xff0c;通过这六个步骤&#xff0c;小白也能搞定机器学习。 看我闪电六连鞭&#xff01;&#x1f923; 数据收集 数据是机器学习的基础&#xff0c;没有数据一切都是空谈&#xff01;数据集的数据量和数据的质量往往决…

潮牌搭配APP的设计与实现

摘 要&#xff1a;本文开发过程以android为中心&#xff0c;通过数据库进行的数据访问操作。软件以面向对象的思维进行开发和设计&#xff0c;针对于广大群众进行下载使用&#xff0c;对用户提供了时尚的搭配&#xff0c;带领用户形成自己的一套搭配系统&#xff0c;做自己搭配…

C++ std::thread 与Qt qthread多线程混合编程

C与Qt深度融合&#xff1a;高效设计多线程应用框架 1. C与Qt线程的混合使用1.1 C线程与Qt线程的基本概念1.2 线程间的相互依赖关系1.3 设计合理的代码框架 二、深入理解C和Qt线程模型2.1 C线程模型2.2 Qt线程模型2.3 C和Qt线程模型的比较 三、C和Qt线程间的互操作性3.1 std::th…

Web应用技术(第十六周/END)

本次练习基于how2j的教程完成对SpringBoot的初步学习。 初识Springboot 学习导入&#xff1a;1.第一个基于SpringBoot的项目&#xff1a;&#xff08;1&#xff09;application.java&#xff1a;该文件中的核心代码&#xff1a; &#xff08;2&#xff09;HelloController.java…

浅结代码混淆2

文章目录 SMC 自解码什么是SMC&#xff1f;原理示例动调 &#xff4d;ov混淆 SMC 自解码 什么是SMC&#xff1f; 简而言之&#xff0c;就是程序中的部分代码在运行之前被加密成一段数据&#xff0c;不可反编译&#xff0c;通过程序运行后执行相关的解码功能&#xff0c;对加密…

Nginx搭建Https反向代理,使用阿里云免费SSL证书 - Docker

Docker安装Nginx - 需要有域名 没有docker需提前安装docker&#xff0c;不知怎么安装的请自行百度。 1、拉取镜像 docker pull nginx2、去阿里云或者其他云服务提供商申请免费证书&#xff0c;申请到之后下载下来&#xff0c;上传到服务器 # 创建nginx-proxy目录 mkdir ngi…

软考A计划-系统架构师-官方考试指定教程-(13/15)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

IP签名档PHP开源版:轻松打造网站个性签名档

今天&#xff0c;我们将为大家介绍一个有趣的IP签名档项目。通过将源代码部署在服务器上&#xff0c;您可以轻松地为自己的社交媒体、论坛等地创建一个独特的签名档&#xff0c;使您的网站更加出彩&#xff01; 接下来&#xff0c;我们将详细向大家展示如何搭建PHP开源版IP签名…

ASP.NET Core Web API入门之一:创建新项目

ASP.NET Core Web API入门之一&#xff1a;创建新项目 一、引言二、创建新项目三、加入Startup类&#xff0c;并替换Program.cs内容四、编辑Program.cs代码五、修改控制器的路由六、运行项目 一、引言 最近闲着&#xff0c;想着没真正从0-1开发过ASP.NET Core Web API的项目&a…

SpringMVC原理分析 | Hello程序

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; SpringMVC Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架&#xff0c;本质上相当于 Servlet&#xff1b; 拥有结构最清晰的 ServletJSPJav…

uni-app APP、html引入html2canvas截图以及截长图

下载安装html2canvas 方式一&#xff0c;https://www.bootcdn.cn/ CDN网站下载html2canvas插件 这里下载后放在测项目目录common下面 页面中引入 方式二、npm方式安装html2canvas 1、npm方式下载 npm i html2canvas2、引入html2canvas import html2canvas from html2can…

linux系统CAN驱动问题分析

在TI sam3354芯片上&#xff0c;使用3.13及4.19版内核&#xff0c;编译CAN驱动&#xff0c;加载启动后&#xff0c;查看有CAN设备&#xff0c;但无法直接使用ifconfig操作CAN设备&#xff0c;以下简单分析下问题。 加载驱动后&#xff0c;查看网络设备&#xff1a; 可以看到有…