条款24:若所有参数都需类型转换,请为此采用non-member函数

2023-12-07 04:36

本文主要是介绍条款24:若所有参数都需类型转换,请为此采用non-member函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.前言

令classes支持隐式类型转换通常是个糟糕的想法,当然这条规则有其例外,最常见的例外是在建立数值类型时候,假如你设计一个class用来表现有理数,允许整数“隐式转换”为有理数似乎颇为合理。

2.实例分析

假设这样开始你的Rational class:

class Rational{public:Rational(int numerator=0,int denominator=1);//构造函数刻意不为explict,允许int-to-        Rational隐式转换int numerator() const;//分子访问函数int denominator() const;//分母的访问函数private:....
};

在这里,你想支持算数运算,比如加法,乘法等等。但你不确定是否该由member函数,non-member函数,或者由non-member friend函数来实现 。

首先研究一下将operator*写成Rational成员函数的写法:

class Rational{public:...const Rational operator*(const Rational& rhs) const;
};

这个设计能够让你能够将两个有理数以最轻松自在的方式相乘:

Rational oneEighth(1,8);
Rational oneHalf(1,2);
Rational result=oneHalf*oneEighth;//很好
result=result*oneEighth;//很好

如果还想要更多的方式,希望能够支持混合式运算,也就是拿Rational和int相乘。然而当尝试混合式算术时,发现只有一半行的通:

result=oneHalf*2;//很好
result=2*oneHalf;//错误

这不是个好兆头,乘法应该满足交换律。

当你以对应的函数形式重写上述两个式子,问题便一目了然了:

result=oneHal;f.operator*(2);//很好
result=2.operator*(oneHalf);//错误

是的,oneHalf是一个内含operator*函数的class对象,所以编译器调用该函数。然而整数2并没有相应的class,也就没有operator*成员函数。编译器也会尝试寻找可以被以下这般调用的non-member operator*(即在命名空间内或者在global作用域内):

result=operator*(2,oneHalf);//错误

但本例并不存在这样一个接受int和Rational作为参数的non-member operator*,因此查找失败。

再分析下先前成功的调用。注意其第二个参数是整数2,但Rational::operator*需要的实参却是个Rational对象,这里发生了什么是事情呢?

答案是这里发生了所谓的隐式转换。编译器知道你正在传递一个int,而函数需要的是Rationl;但它也知道只要调用Rational构造函数并赋予你所提供的int,就可以变出一个适当的Rational来,于是它就这样做了。换句话说此一调用动作在编译器眼里又点像这样:

const Rational temp(2);//根据2建立一个暂时性的Rational对象
result=oneHalf*temp;//等同于oneHalf.operator*(temp);

当然,只因为涉及non-explict构造函数,编译器才会这样做,如果Rational构造函数是explict,以下语句没有一个可以通过编译:

result=oneHalf*2;//错误,在explict构造函数下,无法将2转换为一个Rational
result=2*oneHalf;//同样的错误

这就很难让Rational class支持混合式算术运算了。

然而我的目标不仅在一致性,也要支持混合式算术运算,也就是希望能有个设计能让以上语句通过编译。我们再回到上述两个语句,为什么即使Rational构造函数不是explict,仍然只有一俄国可以通过编译,另一个确不可以:

result=oneHalf*2;//正确(在non-explict构造函数的情况下)
result=2*oneHalf;//错误

答案是:只有当参数列被列于参数列内,这个参数才是隐式类型转换的合格参与者,地位相当于“被调用之成员函数所隶属的那个对象”-即this对象的那个隐喻参数,绝不是隐式转换的合格参与者。这就是为什么上述第一次调用可以通过编译,第二次调用则不能,因为第一次调用伴随一个放在参数列内的参数,第二次调用则没有。

然而你一定也会想要支持混合式算术运算:让operator*成为一个non-member函数,允许编译器在每一个实参身上执行隐式类型转换:

class Rational{....
};
const Rational operator*(const Rational& lhs,const Rational& rhs)//现在做成一个non-           
{return Rational(lhs.numerator()*rhs.numerator(),lhs.denominator()*rhs.denominator());
}
//member函数
Ratioanl oneFourth(1,4);
Rational result;
result=oneFourth*2;//没问题
result=2*oneFourth;//编译通过

不过还有一点需要担心:即operator*是否应该成为Rational class的一个friend函数呢?

就本例而言答案是否定的,因为operator*可以完全由Rational的public接口完成任务,上面代码已经表明此种做法。这导出一个重要的观察:member函数的反面是non-member函数,不是friend函数。

3.总结

如果你需要为某个函数的所有参数(包括被this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member.

这篇关于条款24:若所有参数都需类型转换,请为此采用non-member函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/464502

相关文章

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

Python中isinstance()函数原理解释及详细用法示例

《Python中isinstance()函数原理解释及详细用法示例》isinstance()是Python内置的一个非常有用的函数,用于检查一个对象是否属于指定的类型或类型元组中的某一个类型,它是Py... 目录python中isinstance()函数原理解释及详细用法指南一、isinstance()函数

python中的高阶函数示例详解

《python中的高阶函数示例详解》在Python中,高阶函数是指接受函数作为参数或返回函数作为结果的函数,下面:本文主要介绍python中高阶函数的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录1.定义2.map函数3.filter函数4.reduce函数5.sorted函数6.自定义高阶函数

Python中的sort方法、sorted函数与lambda表达式及用法详解

《Python中的sort方法、sorted函数与lambda表达式及用法详解》文章对比了Python中list.sort()与sorted()函数的区别,指出sort()原地排序返回None,sor... 目录1. sort()方法1.1 sort()方法1.2 基本语法和参数A. reverse参数B.

C#中通过Response.Headers设置自定义参数的代码示例

《C#中通过Response.Headers设置自定义参数的代码示例》:本文主要介绍C#中通过Response.Headers设置自定义响应头的方法,涵盖基础添加、安全校验、生产实践及调试技巧,强... 目录一、基础设置方法1. 直接添加自定义头2. 批量设置模式二、高级配置技巧1. 安全校验机制2. 类型

Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧

《Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧》本文将通过实际代码示例,深入讲解Python函数的基本用法、返回值特性、全局变量修改以及异常处理技巧,感兴趣的朋友跟随小编一起看看... 目录一、python函数定义与调用1.1 基本函数定义1.2 函数调用二、函数返回值详解2.1 有返

Python Excel 通用筛选函数的实现

《PythonExcel通用筛选函数的实现》本文主要介绍了PythonExcel通用筛选函数的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录案例目的示例数据假定数据来源是字典优化:通用CSV数据处理函数使用说明使用示例注意事项案例目的第一

Python一次性将指定版本所有包上传PyPI镜像解决方案

《Python一次性将指定版本所有包上传PyPI镜像解决方案》本文主要介绍了一个安全、完整、可离线部署的解决方案,用于一次性准备指定Python版本的所有包,然后导出到内网环境,感兴趣的小伙伴可以跟随... 目录为什么需要这个方案完整解决方案1. 项目目录结构2. 创建智能下载脚本3. 创建包清单生成脚本4

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

SpringBoot 获取请求参数的常用注解及用法

《SpringBoot获取请求参数的常用注解及用法》SpringBoot通过@RequestParam、@PathVariable等注解支持从HTTP请求中获取参数,涵盖查询、路径、请求体、头、C... 目录SpringBoot 提供了多种注解来方便地从 HTTP 请求中获取参数以下是主要的注解及其用法:1