函数参数的最佳传递方式与现代C++的规则
函数参数的最佳传递方式与现代C++的规则
在C++中,如何最佳地传递函数参数以及如何处理类的特殊成员函数,一直是优化性能和代码质量的重要话题。下面我将详细解释这些概念。
使用移动语义实现 Swap 函数
移动语义(Move Semantics)能够提升性能的一个例子是实现一个交换(swap)函数模板,该模板交换两个对象。不使用移动语义的实现如下:
template <typename T>
void swapCopy(T& a, T& b) {T temp { a };a = b;b = temp;
}
这种实现方式会影响性能,尤其是当类型T的拷贝开销很大时。使用移动语义,实现可以避免所有拷贝:
template <typename T>
void swapMove(T& a, T& b) {T temp { std::move(a) };a = std::move(b);b = std::move(temp);
}
这就是标准库中 std::swap()
的实现方式。
在返回语句中使用 std::move()
如果返回语句的形式为 return object;
,并且 object
是一个局部变量、函数参数或临时值,编译器会将其视为右值表达式,并触发返回值优化(RVO)。此外,如果 object
是一个局部变量,命名返回值优化(NRVO)也可能发生。RVO和NRVO都是拷贝省略(Copy Elision)的形式,使得从函数返回对象非常高效。
使用 std::move()
来返回对象会怎样呢?无论你写 return object;
还是 return std::move(object);
,在两种情况下,编译器都会将其视为右值表达式。然而,使用 std::move()
,编译器无法再应用RVO或NRVO,这可能会对性能产生重大影响!
所以,请记住以下规则:当从函数返回局部变量或参数时,只需写 return object;
,不要使用 std::move()
。
参数的最佳传递方式
到目前为止,建议对非原始类型的函数参数使用 const 引用参数,以避免不必要的昂贵拷贝。然而,随着右值的引入,情况略有变化。想象一个无论如何都会拷贝其参数的函数。现在,你可能想要添加一个重载,以避免在右值的情况下进行任何拷贝。这里有一个例子:
class DataHolder {
public:void setData(const std::vector<int>& data) {m_data = data;}void setData(std::vector<int>&& data) {m_data = std::move(data);}private:std::vector<int> m_data;
};
但是,有一种更好的方式,涉及使用传值的单个方法。对于函数本来就会拷贝的参数,使用传值语义是最优的选择。如果传入左值,它恰好被拷贝一次。如果传入右值,不会进行拷贝。
零规则(Rule of Zero)
在现代C++中,应该遵循所谓的零规则(Rule of Zero)。这个规则指出,你应该设计你的类,使它们不需要任何特殊的成员函数。怎么做到这一点呢?基本上,你应该避免使用任何老式的动态分配内存。相反,应该使用像标准库容器这样的现代构造。例如,使用 vector<vector<SpreadsheetCell>>
替代 Spreadsheet
类中的 SpreadsheetCell**
数据成员。vector
会自动处理内存,因此不需要任何特殊的成员函数。
现代C++中推荐使用零规则,而五规则(Rule of Five)应该限于自定义的资源获取是初始化(RAII)类。RAII类获取资源的所有权,并在合适的时候处理它的释放。这是一种设计技术,例如,由 vector
和 unique_ptr
使用,并在后续的章节中进一步讨论。
静态方法和 const
方法是 C++ 中的两个重要概念,它们各自在不同的情况下发挥着重要作用。
静态方法(Static Methods)
静态方法是那些不依赖于类的实例而存在的方法。与静态数据成员类似,静态方法适用于整个类,而不是每个对象。在实现静态方法时,需要注意以下几点:
- 静态方法不是针对特定对象调用的,因此它们没有
this
指针,也无法访问类的非静态成员。 - 静态方法可以访问类的私有和保护的静态成员,也可以在具有相同类型的对象上访问私有和保护的非静态成员,前提是这些对象对静态方法可见(例如,通过作为参数传递对象的引用或指针)。
- 在类内部的任何方法中,可以像调用常规成员函数一样调用静态方法。在类外部,需要使用作用域解析操作符(
::
)并带上类名来调用静态方法。
例如:
Foo::bar();
const 方法(Const Methods)
const
方法是保证不会修改任何数据成员的方法。如果你有一个 const
对象,引用到 const
或指向 const
的指针,编译器不允许你调用除非是 const
方法的任何方法。通过在方法声明时使用 const
关键字,可以保证该方法不会修改任何数据成员。
例如:
double SpreadsheetCell::getValue() const {return m_value;
}std::string SpreadsheetCell::getString() const {return doubleToString(m_value);
}
- 在
const
方法内部,所有数据成员都被视为const
,因此如果尝试修改数据成员,编译器会报错。 - 不能将静态方法声明为
const
,因为这是多余的。静态方法没有类的实例,因此它们无法更改内部值。 - 在非
const
对象上可以调用const
和非const
方法。然而,只能在const
对象上调用const
方法。
mutable 数据成员(Mutable Data Members)
有时,你可能会编写一个在逻辑上是 const
的方法,但该方法恰好会更改对象的某个数据成员。这种修改对用户可见的数据没有影响,但从技术上讲是一种更改,因此编译器不允许你将方法声明为 const
。在这种情况下,可以使用 mutable
关键字来声明那些即使在 const
方法中也可以被修改的数据成员。
例如:
class SpreadsheetCell {// ...
private:double m_value { 0 };mutable size_t m_numAccesses { 0 };// ...
};double SpreadsheetCell::getValue() const {m_numAccesses++;return m_value;
}std::string SpreadsheetCell::getString() const {m_numAccesses++;return doubleToString(m_value);
}
在这个例子中,即使 getValue()
和 getString()
被标记为 const
,它们也可以修改 m_numAccesses
,因为它被声明为 mutable
。这允许方法在保持其 const
性质的同时,对某些数据成员进行修改。
参考:Professional C++ 5Th Edition
公众号:coding日记
相关文章:
函数参数的最佳传递方式与现代C++的规则
函数参数的最佳传递方式与现代C的规则 在C中,如何最佳地传递函数参数以及如何处理类的特殊成员函数,一直是优化性能和代码质量的重要话题。下面我将详细解释这些概念。 使用移动语义实现 Swap 函数 移动语义(Move Semantics)能…...

Asterisk Ubuntu 安装
更新环境 sudo apt update sudo apt install wget build-essential git autoconf subversion pkg-config libtool sudo contrib/scripts/get_mp3_source.sh A addons/mp3 A addons/mp3/common.c A addons/mp3/huffman.h A addons/mp3/tabinit.c A addons/mp3/Ma…...

rwkv模型lora微调之accelerate和deepspeed训练加速
目录 一、rwkv模型简介 二、lora原理简介 三、rwkv-lora微调 1、数据整理 2、环境搭建 a、Dockerfile编写 b、制造镜像 c、容器启动 3、训练代码修改 四、模型推理 1、模型推理 2、lora权重合并 3、推理web服务 五、总结 由于业务采用的ChatGLM模型推理成本太大了…...

分享一下在微信小程序里怎么做一个投票链接
在当今信息化社会,投票已成为各行各业收集意见、汇聚智慧的重要手段。传统的投票方式往往需要投入大量人力物力,而如今,借助微信小程序,我们可以在几分钟内创建一个高效、便捷的投票平台。本文将详细介绍如何在微信小程序中添加投…...
v-model语法糖
v-model原理 v-model实现双向绑定的语法糖,常用于表单与组件之间的数据双向绑定v-model本质上是 value属性和input事件的一层包装 v-model的作用:提供数据的双向绑定数据发生了改变,页面会自动变 v-bind:value页面输入改变 , 数据…...

纷享销客荣获最佳制造业数字营销服务商奖
2023年10月26日,第二届中国制造业数智化发展大会在上海盛大召开。本次大会汇聚了制造行业的顶尖企业和专家,共同探讨如何通过数字化转型赋能企业自身成长,实现信息化向数字化的升级转型。 在本次盛会上,纷享销客以其卓越的基本面、…...

蓝桥杯每日一题2023.11.3
题目描述 承压计算 - 蓝桥云课 (lanqiao.cn) 题目分析 将重量存入a中,每一层从上到下进行计算,用d进行计算列的重量,当前d的重量应为正上数组和右上数组的个半和并加上自身的重量 计算到30层记录最大最小值,进行比例运算即可 …...
中国电子云-隐私计算-云原生安全可信计算,物理-硬件-系统-云产品-云平台,数据安全防护
目录 联邦学习的架构思想 中国电子云-隐私计算-云原生安全...

PHP服务器端电商API原理及示例讲解(电商接口开发/接入)
下面小编就为大家分享一篇PHP服务器端API原理及示例讲解(接口开发),具有很好的参考价值,希望对大家有所帮助 相信大家都做过PHP请求电商API接口获取数据,比如淘宝平台商品API接口,订单接口,京东接口,1688接…...

Spring Cloud应用- Eureka原理、搭建
初期对Spring Cloud的学习以应用搭建为主,所以内容不会太枯燥。 一直以来,自以为Spring全家桶的学习中,Spring framework是基础中的基础,部分内容也还是必须要读源码去理解底层原理,SpringMVC、SpringBoot,…...
Servlet 设置启动时机(web.xml方式和@WebServlet方式)
1、通过web.xml方式 5)Servlet的启动时机 - 默认情况下,servlet是不会随着容器的启动而被实例化的,只有当第一次给我发请求时才会被实例化那么,这种情况对于第一次请求是不公平的因此,为了提高用户体验度,提高服务器的…...

一个使用uniapp+vue3+ts+pinia+uview-plus开发小程序的基础模板
uniappuviewPlusvue3tspiniavite 开发基础模板 使用 uniapp vue3 ts pinia vite 开发基础模板,拿来即可使用,不要删除 yarn.lock 文件,否则会启动报错,这个可能和 pinia 的版本有关,所以不要随意修改。 拉取代码…...

Kali安装docker
第一步:kali添加Docker官方的GPG密钥 curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add 第二步:进入root更新源: su rootecho ‘deb https://download.docker.com/linux/debian stretch stable’> /etc/ap…...
Maven第七章:Maven工程最佳实践
Maven第七章:Maven工程最佳实践 前言 本章重点,通过一个maven工程最佳实践案例,熟悉和掌握maven在项目中的应用基本思路,让你的技能值瞬间暴涨。 最佳实践 确定项目的坐标和依赖 在Maven中,项目的坐标定义了项目的唯一标识符,包括groupId、artifactId和version。因此,在…...
【深度学习】【pytorch】对卷积层置零卷积核进行真实剪枝
最近需要对深度学习模型进行部署,因此需要对模型进行压缩,博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 前言卷积层剪枝总结 前言 深度学习剪枝(Pruning)是一种用于减少神经网络模型大小、减少计算量和提高推理效率的技术,通过去除神经…...

机器人仿真-gazebo学习笔记(3)URDF和机器人模型
1.URDF简介 URDF(统一机器人麦哦书格式)是ROS中的重要机器人模型描述格式,ROS提供了URDF文件的c解析器,可以解析URDF文件中使用XML格式的机器人模型。 urdf - ROS Wiki 自己查阅ros官方对URDF的介绍其实会强于大部分网上流传的文章。 1.URDF文件常用的…...

lua-resty-request库写入爬虫ip实现数据抓取
根据提供的引用内容,正确的库名称应该是lua-resty-http,而不是lua-resty-request。使用lua-resty-http库可以方便地进行爬虫,需要先安装OpenResty和lua-resty-http库,并将其引入到Lua脚本中。然后,可以使用lua-resty-h…...
gitlab Activating and deactivating users
原文:Redirecting... Deactivating a userActivating a user Activating and deactivating users GitLab 管理员可以停用和激活用户. Deactivating a user 在 GitLab 12.4 中引入 . 为了临时阻止没有最近活动的 GitLab 用户访问,管理员可以选择停用…...

linux入门到精通-第五章-动态库和静态库
目录 参考概述1、静态链接2 、动态链接3 、静态、动态编译对比 静态库和动态库简介传统编译 静态库制作和使用1、创建静态库的过程2、使用静态库 动态库制作和使用1、创建动态库的过程1)、生成目标文件,此时要加编译选项:-fPIC (f…...
markdown 如何更改字体以及颜色等功能
markdown 是IT人士写文档的常用方式,但是markdown默认又不支持颜色字体等特殊功能,所以呢想实现字体颜色高亮等特殊功能,实现的方法呢就是使用HTML,所以将部分文字改成HTML代码就行 颜色 <font color#0099ff>color #0099f…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...