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

news2024/10/2 21:24:05

2.4.8、使用Move语法实现Swap函数

        move语法提升性能的又一个例子,使用swap()函数交换两个对象。下面的swapCopy()的实现没有使用move语法:

void swapCopy(Object& a, Object& b)
{
    Object temp { a };
    a = b;
    b = temp;
}

        首先,a被拷贝到temp,然后b被拷贝到a,最后temp被拷贝到b。如果对象拷贝的消耗比较大的话,该实现就会有比较糟糕的性能。使用move语法,实现就可以避免掉所有的拷贝:

void swapMove(Object& a, Object& b)
{
    Object temp { std::move(a) };
    a = std::move(b);
    b = std::move(temp);
}

        这就是标准库中std::swap()的实现。

2.4.9、在return语句中使用std::move()

        从c++17开始,对于return object语句编译器不再允许执行任何拷贝或者移动对象;其中object是一个没有命名的临时对象。这叫做mandatory elision of copy/move,意思是通过值返回对象就不会有任何性能损失。如果object是一个本地变量而不是一个函数参数,允许非mandatory elision of copy/move operations,其优化叫做named return value optimization(NRVO)。标准c++不对该优化进行保证。有些编译器只在发行构建中而不是在debug构建中执行该优化。对于强制或非强制省略,编译器可以避免从函数返回的对象拷贝。这样做的结果就是zero-copy pass-by-value语法。

        警告:记住对于NRVO,即使copy/move构造函数不被调用,仍然需要是可访问的;否则的话,根据标准,程序的格式就是不正确的。

        那么,当使用std::move()返回一个对象会出现什么情况呢?看一下下面的代码:

return std::move(object);

        这行代码,编译器无法应用强制或非强制(NRVO)copy/move操作的省略,因为只有在return object格式时才管用。既然copy/move省略无法再使用,编译器的下一个选项就是使用move语法,如果对象支持的话,如果不支持,就会使用copy的语法。

        与NRVO相比,倒退到move语法会有一小部分性能影响,但是倒退到copy语法可能会有较大的性能影响!所以,记住如下的警告:

        警告:如果从函数中返回一个本地变量或者没有命名的临时变量,只要写return object就好了,千万不要使用std::move()。

        记住,如果想要从一个成员函数中返回类的一个数据成员,如果想要把它移出来而不是返回一个拷贝的话就需要使用std::move()。

        还有,对于像下面这样的表达式要细心:

return condition ? obj1 : obj2;

        这不是return object;的形式,编译器不会应用copy/move省略。更糟糕的是,condition ? obj1 : obj2形式的表达式是一个左值,所以编译器使用拷贝构造函数而不是返回一个对象。为了至少触发move语法,可以重写return语句如下:

return condition ? std::move(obj1) : std::move(obj2);

        或者

return std::move(condition ? obj1 : obj2);

        然而,如下重写return语句会更清晰,因为编译器可以自动使用move语法而不用显式地使用std::move():

if (condition) {
    return obj1;
} else {
    return obj2;
}

2.4.10、传递参数给函数的更优的方式

        到现在为止,建议都是对于非原始函数参数使用reference-to-const参数以避免不必要的昂贵的对于传递给函数的参数的拷贝。然而,对于混合情况使用右值,情况改善不大。相像一下,一个函数,不管怎么样都要拷贝一个参数传递给其中的一个参数。这种情况常见于类成员函数。下面是一个简单的例子:

class DataHolder
{
public:
    void setData(const vector<int>& data) { m_data = data; }

private:
    vector<int> m_data;
};

        setData()会对传入的参数做一个拷贝。现在你已经对于右值以及右值引用很熟练了,可以会想添加一个重载来优化setData()以避免右值情况下的任何拷贝。举例如下:

class DataHolder
{
public:
    void setData(const vector<int>& data) { m_data = data; }
    void setData(vector<int>&& data) { m_data = move(data); }

private:
    vector<int> m_data;
};

        当用临时变量调用setData()时,不会生成拷贝;数据用move取代。

        下面的代码段触发了一个对于reference-to-const setData()重载的调用,因此生成了一个数据的拷贝:

DataHolder wrapper;
vector myData { 11, 22, 33 };
wrapper.setData(myData);

        另一方面,下面的代码段使用临时变量调用setData(),它触发了一个对于右值引用的setData()重载的调用。数据按顺序move而不是copy。

wrapper.setData({ 22, 33, 44 });

        不幸的是,这种方式优化setData()需要对左值与右值都进行重载的实现。幸运的是,有一种更好的方式来对单个成员函数进行优化,那就是使用pass-by-value。是的,pass-by-value!到目前为止,都是建议使用reference-to-const参数来你看期间任何不必要的拷贝,但是现在我们要建议使用pass-by-value了。明确一下。对于不拷贝的参数,传递reference-to-const仍然有效。pass-by-value建议只适于于不管怎么样函数都要进行拷贝的参数。在这种情况下,使用pass-by-value语法,代码对左值与右值都是优化的。如果传递左值,只拷贝一次,与reference-to-const参数一样。但是,如果传递的是右值,就不需要拷贝,与右值引用参数一样。让我们来看一下代码:

class DataHolder
{
public:
    void setData(vector<int> data) { m_data = move(data); }

private:
    vector<int> m_data;
};

        如果左值传递给setData(),会把数据拷贝到data参数,然后移到m_data。如果右值传递给setData(),它会移到data参数,再移给m_data。

        注意:对于函数要拷贝的参数使用pass-by-value,但是只在参数是一个支持move语法的类型时,并且不需要参数有多态行为的时候。否则,要使用reference-to-const参数。通过值传递多态类型会导致分片。这个我们以后再讨论吧。

2.5、零规则

        在本章的一开始介绍过五规则。它指出,一旦声明五个特别成员函数(析构函数、拷贝构造函数、move构造函数,拷贝赋值操作符与move赋值操作符)的一个,要通过实现,缺省或者删除它们来进行全部声明。原因是编译器有复杂的规则来决定 是否要自动提供一个这些特殊成员函数编译器生成的版本。通过自己声明全部,就不需要让编译器决定,使你的意图更清晰。

        到目前为止,所有的讨论都是解释怎么写这五个特殊的成员函数。然而,在现代c++中,也可以应用零规则。

        零规则指出,你可以用这样的一种方式来设计类,不要求那些五个特殊成员函数中的任何一个。怎么做到这一点呢?可以对非多态类型的来做,避免使用旧风格的动态分配内存或其他资源。反过来,使用像标准库容器这样的现代构造函数与智能指针。例如,可以使用vector<vector<SpreadsheetCell>>而不是SpreadsheetCell**数据成员在Spreadsheet类中。或者更好的是,使用vector<SpreadsheetCell>保存线性的spreadsheet的代表。vector自动处理内存,所以没有任何五个特殊成员函数的必要。

        警告:在现代c++中,使用零规则!

        五规则应该仅限于客户化资源获得即初始化(RAII)类中。RAII类承担了资源的属主,在合适的时间处理释放。是一个用于设计的技巧,例如,以前提到过的vector与unique_ptr。还有,多态类型也要求遵从五规则。

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

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

相关文章

Bruno:拥有 11.2k star 的免费开源 API 测试工具

Github 开源地址&#xff1a; https://github.com/usebruno/bruno 官网地址&#xff1a; https://www.usebruno.com/ 下载地址&#xff1a; https://www.usebruno.com/downloads 使用文档&#xff1a; https://docs.usebruno.com/ Bruno 是一款全新且创新的 API 客户端&…

微调学习记录

目前看到的市面上的微调文章&#xff0c;想大体上给他们分个类&#xff0c;方便后续进行重点学习 参数高效微调 1. LoRA 不用多说含金量 2. Rein https://github.com/w1oves/rein 把它也算进来了&#xff0c;类似。 Adapter adapter类的我感觉都大差不差&#xff0c;具体…

VisionTS:基于时间序列的图形构建高性能时间序列预测模型,利用图像信息进行时间序列预测

构建预训练时间序列模型时面临的主要挑战是什么&#xff1f;获取高质量、多样化的时间序列数据。目前构建基础预测模型主要有两种方法&#xff1a; 迁移学习LLM&#xff1a;通过针对时间序列任务定制的微调或分词策略&#xff0c;重新利用预训练的大型语言模型&#xff08;LLM…

餐饮重点企业在AI领域的布局,看方大的AI实践

大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 AI已经被应用在餐饮餐厨行业的哪些方面&am…

Spring注解系列 - @Autowired注解

文章目录 使用总结注入原理Autowired 注入过程InjectionMetadataInjectedElement依赖注入查找过程findAutowireCandidates 缓存注入信息 Resource 注解 使用总结 Autowired注解可以自动将所需的依赖对象注入到类的属性、构造方法或方法中&#xff0c;从而减少手动注入依赖的代…

Android Compose的基本使用

前言: Compose这个东西呢,好处我没发现,坏处就是学习成本和低版本兼容. 不过,看在官方力推的份儿上,有空就学一下吧. 当初的kotlin,很多人说鸡肋(包括我)!现在不也咔咔用纯kotlin做项目吗?哈哈哈哈. 未来的事情,谁说得清呢? 首先创建一个专用的Compose项目 对没错!看到E…

体系结构论文(五十三):Featherweight Soft Error Resilience for GPUs 【22‘ MIRCO】

Featherweight Soft Error Resilience for GPUs 一、文章介绍 背景&#xff1a;软错误通常由高能粒子&#xff08;如宇宙射线和α粒子&#xff09;打击电路造成的位翻转&#xff0c;可能导致程序崩溃或产生错误输出。随着电子技术的进步&#xff0c;电路对这种辐射引发的软错…

Arduino UNO R3自学笔记14 之 Arduino使用HC-SR04模块如何测量距离?

注意&#xff1a;学习和写作过程中&#xff0c;部分资料搜集于互联网&#xff0c;如有侵权请联系删除。 前言&#xff1a;学习使用HC-SR04模块测距。 1.HC-SR04模块介绍 基本参数&#xff1a; ●使用电压&#xff1a;DC---5V ●静态电流&#xff1a;小于2mA ●电平输出&#…

【计算机网络】传输层UDP和TCP协议

目录 再谈端口号端口号范围划分认识知名端口号查看知名端口号两个问题 UDP协议UDP特点UDP的缓冲区基于UDP的应用层协议 TCP协议TCP协议格式确认应答机制超时重传机制连接管理机制&#xff08;三次握手与四次挥手&#xff09;理解TIME_WAIT状态理解CLOSE_WAIT状态滑动窗口快重传…

wsl(1) -- win11环境配置

1.前言 本专栏主要记录了我配置wsl的过程&#xff0c;以便日后回忆。 2. 开启WSL可选功能 打开设置&#xff0c;点击应用&#xff0c;点击可选功能&#xff0c;点击更多Windows功能&#xff0c;查看是否开启了【适用于Linux的Windows子系统】和【虚拟机平台】 3. 更新wsl …

【JavaEE初阶】深入理解多线程阻塞队列的原理,如何实现生产者-消费者模型,以及服务器崩掉原因!!!

前言&#xff1a; &#x1f308;上期博客&#xff1a;【JavaEE初阶】深入解析单例模式中的饿汉模式&#xff0c;懒汉模式的实现以及线程安全问题-CSDN博客 &#x1f525;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 ⭐️小编会在后端开发的学习中不断更新~~…

【在Linux世界中追寻伟大的One Piece】System V共享内存

目录 1 -> System V共享内存 1.1 -> 共享内存数据结构 1.2 -> 共享内存函数 1.2.1 -> shmget函数 1.2.2 -> shmot函数 1.2.3 -> shmdt函数 1.2.4 -> shmctl函数 1.3 -> 实例代码 2 -> System V消息队列 3 -> System V信号量 1 -> Sy…

K8S部署流程

一、war打包镜像(survey,analytics,trac系统) 代码打包成war准备tomcat的server.xml文件&#xff0c;修改connector中8080端口为项目的端口 修改前&#xff1a; <Connector port"8080" protocol"HTTP/1.1"connectionTimeout"20000"redirect…

idea环境下vue2升级vue3

在IDEA环境下&#xff0c;Vue2升级Vue3是一个非常重要的主题。在本文中&#xff0c;我们将介绍Vue2和Vue3之间的主要区别&#xff0c;以及如何在IDEA中升级Vue2项目到Vue3。我们还将讨论Vue3的新特性&#xff0c;如Composition API和Teleport等&#xff0c;并提供一些实用的代码…

快速掌握-vue3

是什么 vue2 的升级版&#xff0c; 使用 ts 重构了代码&#xff0c; 带来了 Composition API RFC。 类似于 react hook 的写法。 ts 重构&#xff0c;代码可读性更强vue3.x 使用 Proxy 取代 Vue2.x 版本的 Object.defineProperty实现了 TreeShaking (当 Javascript 项目达到一定…

自闭症寄宿学校:为孩子发掘多重才能

在教育的广阔天地里&#xff0c;每一片土壤都孕育着不同的生命&#xff0c;每一颗种子都蕴含着无限的可能。对于自闭症儿童而言&#xff0c;他们的世界或许更加独特与复杂&#xff0c;但同样充满了未被发掘的潜能与才华。在广州&#xff0c;星贝育园自闭症儿童寄宿制学校正以满…

计算机毕业设计 Java酷听音乐系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

师生健康信息管理:SpringBoot技术突破

第4章 系统设计 4.1 系统体系结构 师生健康信息管理系统的结构图4-1所示&#xff1a; 图4-1 系统结构 登录系统结构图&#xff0c;如图4-2所示&#xff1a; 图4-2 登录结构图 师生健康信息管理系统结构图&#xff0c;如图4-3所示。 图4-3 师生健康信息管理系统结构图 4.2…

【Linux】用虚拟机配置Ubuntu环境

目录 1.虚拟机安装Ubuntu系统 2.Ubuntu系统的网络配置 3.特别声明 首先我们先要下载VMware软件&#xff0c;大家自己去下啊&#xff01; 1.虚拟机安装Ubuntu系统 我们进去之后点击创建新的虚拟机&#xff0c;然后选择自定义 接着点下一步 再点下一步 进入这个界面之后&…

element-ui 通过按钮式触发日期选择器

element ui 写在前面1. 自定义的日期时间组件CustomDatePicker.vue2. 页面效果总结写在最后 写在前面 需求&#xff1a;elementui中日期时间选择器&#xff0c;目前只能通过点击input输入框触发日期选择器&#xff0c;我希望能通过其他方式触发日期选择器同时把input输入框去掉…