【复读EffectiveC++24】条款24:若所有参数皆需类型转换,请为此采用non-member函数
条款24:若所有参数皆需类型转换,请为此采用non-member函数
一、问题引入
举个例子,如果你设计一个表示有理数的类,允许从整型到有理数的隐式转换应该是合理的。在C++内置类型中,从int转换到double也是再合理不过的了(比从double转换到int更加合理)。看下面的例子:
class Rational
{
public://构造函数未设置为explicit,因为我们希望一个int可以隐式转换为RationalRational(int numerator = 0, int denominator = 1);int numerator()const;int denominator()const; const Rational operator*(const Rational& rhs)const;
private:...
};
你想支持有理数的算术运算,比如加法,乘法等等,跟随直觉,我们将函数放进相关 class 内(有时会与面向对象守则发生矛盾,详见条款23),会发生什么?
Rational oneEighth(1, 8);
Rational oneHalf(1, 2);
Rational result = oneHalf*oneEighth;//正确
result = result*oneEighth; //正确
看到以上结果,也许会觉得满足了,但当你进一步尝试混合模式的运算的时候,你会发现只有一半的操作是对的:
Rational res = oneHalf * 2;//正确
Rational result = 2 * oneHalf; //错误
为什么错误?
二、归因分析
将上面的例子用等价的函数形式写出来,你就会知道问题出在哪里:
result = oneHalf.operator*(2); // fine
result = 2.operator*(oneHalf ); // error!
在此分析:
- 第一个能通过,其原因在于发生了隐式类型转换,编译器知道函数需要 Rational 类型,但你传递了 int 类型的实参,它们也同样知道通过调用 Rational 的构造函数,可以将你提供的 int 实参转换成一个 Rational 类型实参,这就是编译器所做的。类似于:
const Rational temp(2); // 创建一个临时变量
result = oneHalf * temp; // 等同于oneHalf.operator*(temp);
- 第二不能通过,其原因在于 oneHalf 对象是 Rational 类的一个实例,而 Rational 支持 operator 操作,所以编译器能调用这个函数。然而,整型 2 却没有关联的类,也就没有 operator 成员函数。编译器实际会去寻找非成员operator*函数,例如:
result = operator*(2, oneHalf );
因此,为了支持混合模式的运算和满足一致性,为了解决 只有参数列表中的参数才有资格进行隐式类型转换,而 this 指针指向的那个,没有资格进行隐式类型转换 的问题,就要采用non-member函数。
三、解决方案
例如,下面将 operator*() 函数变为一个非成员函数:
class Rational
{
public:Rational(int numerator = 0, int denominator = 1);int numerator()const;int denominator()const;
private:...
};const Rational operator*(const Rational& lhs,const Rational& rhs)
{return Rational(lhs.numerator()*rhs.numerator(),lhs.denominator()*rhs.denominator());
}
使用后,结果如下:
Rational oneFourth(1, 4);
Rational result;
result = oneFourth* 2;
result = 2 * oneFourth;
问题解决,但还有点要注意:operator* 是否该成为Rational class的一个友元函数呢?
答案是否定的,因为 operator* 可以完全依靠Rational的public接口来实现。上面的代码就是一种实现方式。我们能得到一个很重要的结论:成员函数的反义词是非成员函数而不是友元函数。
太多的C++程序员认为一个类中的函数如果不是一个成员函数(举个例子,需要为所有参数做类型转换),那么他就应该是一个友元函数。
上面的例子表明这样的推理是有缺陷的。尽量避免使用友元函数,就像生活中的例子,朋友带来的麻烦可能比从它们身上得到的帮助要多。
四、总结
如果你需要为某个函数的所有参数(包括被this这孩子很所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member。
相关文章:
【复读EffectiveC++24】条款24:若所有参数皆需类型转换,请为此采用non-member函数
条款24:若所有参数皆需类型转换,请为此采用non-member函数 一、问题引入 举个例子,如果你设计一个表示有理数的类,允许从整型到有理数的隐式转换应该是合理的。在C内置类型中,从int转换到double也是再合理不过的了&a…...

Mac应用快速启动器:Alfred 5 for Mac 激活版
Alfred 5 是一款专为 macOS 系统设计的效率提升工具。这款软件以其快速启动和高效操作功能著称,通过使用快捷键来呼出输入界面,用户可以快速完成各种任务。 最新版本 Alfred 5.5 引入了一些新功能。其中包括整合了 ChatGPT 和 DALL-E,这意味…...

oracle语法介绍
Oracle数据库是关系型数据库管理系统之一,其SQL语法遵循标准的SQL规范,但也有一些自己的扩展。以下是一些Oracle SQL语法的基本示例: 1.选择数据: SELECT * FROM my_table; 1.插入数据: INSERT INTO my_table (colum…...

Python IDLE修改JetBrains Mono字体教程
自己在使用Python IDLE过程中发现原生字体不好看,不美观。尤其是对于部分字符,l打印不美观,区别不明显。于是诞生了换字体的想法。 教程简单,快速,3-5分钟不到即可完成。 目录 选型 下载安装 使用 选型 考虑到代码…...
CCF编程能力等级认证GESP—C++1级—20240629
CCF编程能力等级认证GESP—C1级—20240629 单选题(每题 2 分,共 30 分)判断题(每题 2 分,共 20 分)编程题 (每题 25 分,共 50 分)休息时间立方数 单选题(每题 2 分,共 30…...
继HBM之后, 内存领域新宠MCR DIMM闪亮登场!
随着人工智能(AI)和大数据的迅速发展,新型DRAM正迎来新的发展机遇。在服务器需求的推动下,MCRDIMM作为内存行业的新宠儿,正逐步登上历史舞台。 扩展阅读:MCR DIMM如何解决内存带宽瓶颈? MCR DIM…...

谷粒商城实战笔记-75-商品服务-API-品牌管理-品牌分类关联与级联更新
文章目录 一,引入Mybatis Plus分页插件二,品牌列表的模糊查询三,增加品牌测试数据四,开发后台品牌关联分类接口1,接口product/categorybrandrelation/catelog/list2,接口product/categorybrandrelation/sav…...
Java中的equals()与==的区别与用法
1. 区别 “”操作符用于比较两个对象的地址是否相等。.equals() 方法用于比较两个对象的内容是否相等。 Object 类的 .equals() 方法默认采用的是“”操作符进行比较。假如子类没有重写该方法的话,那么“”操作符和 .equals() 方法的功效就完全一样——比较两个对…...

【ai】 2005年 rule based expert system学习笔记1
PPT 是2005年的? Negnevitsky, Pearson Education 使用两种推理引擎的选择 backward chaining(逆向链接)推理过程 backward chaining(逆向链接)推理过程的GPT解释 这幅图展示了一个基于规则的专家系统如何通过backward chaining(逆向链接)推理过程来达到最终的推理目标…...

AI写作|去除了AI味道,我还花2分钟动手制作了一个coze智能体
本文背景: AI写出来的东西,机器味太浓? AI生成的文章内容质量不稳定、因为依赖于已有的数据和模式,AI可能很难创作出具有深度见解或独创性的内容 AI还无法完全理解复杂的上下文关系,导致生成的内容与用户期望的上下文不…...
数据集相关类代码回顾理解 | utils.make_grid\list comprehension\np.transpose
目录 utils.make_grid list comprehension np.transpose utils.make_grid x_gridutils.make_grid(x_grid, nrow4, padding2) make_grid 函数来自torchvision的utils模块,用于图像数据可视化,将一批图像排列成一个网格。 x_grid:四维图像…...
React前端面试每日一试 3.状态(State)和属性(Props)的区别是什么?
加粗样式先简单介绍一下Props和State的特点 Props(属性) Props(Properties)是React组件间传递数据的一种方式。它们是从父组件传递给子组件的只读数据,子组件不能修改这些数据。Props主要用于配置组件,使…...
射灯怎么安装才好看,射灯安装防踩坑
射灯安装的5个尺寸,不懂容易踩坑 你得选好角度,算好安装距离 为了防止我们花了钱却装不出效果 1,射灯是可以调角度的,一般选24度和36度就行 像小的装饰画可以选24度,大的装饰画选36度 也就是重点照明选24,洗墙和打造小山丘36度 …...
Mojo变量详解
变量是一个保存值或对象的名称。Mojo中的所有变量都是可变的 - 它们的值可以改变。(如果您想定义一个在运行时无法更改的常量值,请参见alias关键字。) Mojo曾经支持使用let关键字来声明不可变变量。为了简化语言,并出于其他原因,已经将其移除 ( 为何移除let)。为了简化…...
ElasticSearch 面试题及答案整理,最新面试题
Elasticsearch中的倒排索引是什么?它如何工作? 倒排索引是Elasticsearch中用于快速全文搜索的关键数据结构。它的工作原理包括: 1、索引创建: 对文档中的每个唯一单词创建一个索引条目。 2、文档列表: 每个索引条目都指向包含该单词的文档列表。 3、快速查找: 在搜索时,…...

Java基本语法学习的案例练习
本文是在学习过C语言后,开始进行Java学习时,对于基本语法的一些案例练习。案例内容来自B站黑马编程课 1.HelloWorld 问题介绍;请编写程序输出“HelloWorld”. public class HelloWorld { public static void main(String[] args) { System.out.print…...

FPGA实现LCD12864控制
目录 注意! a) 本工程采用野火征途PRO开发板,外接LCD12864部件进行测试。 b) 有偿提供代码!!!可以定制功能!!!有需要私信!!! c) 本文测试采用…...
mysql 批量执行sql语句脚本
有时候我们需要批量执行多个数据库的创建和数据创建执行可以通过下面脚本批量创建和执行脚本。我们只需要在sql命令行或者客户端执行下面一个脚本批量创建执行多个库的创建和执行 xxxxinit.sql create user root% identified by test; mysql -h 192.168.17.7 -u root -p mysq…...
【Git】Git概述
一、Git的基本概念和特点 基本概念: 仓库(Repository):Git存储代码的基本单位,包含项目的所有文件和历史提交记录。Git支持本地仓库和远程仓库,本地仓库存储在开发者的计算机上,而远程仓库通常…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...