条款24:若所有参数皆需类型转换,请为此采用non-member函数
发布日期:2021-05-07 03:15:39 浏览次数:29 分类:精选文章

本文共 2155 字,大约阅读时间需要 7 分钟。

1、问题引出

通常,令class支持隐式转换是不好的设计。但是也有例外,最常见的例外是在建立数值类型时。例如设计一个类表示有理数时,允许整数隐式转换为有理数是合理的。

class Rational{   public:    Rational(int numerator = 0, int denominator = 1); //刻意不为explicit;允许int-to-Rational隐式转换    int numerator()const;    int denominator()const;};

我需要为有理数类实现加法、乘法等,是该写成member函数、non-member、non-friend函数还是non-member、friend函数呢?

答案是:non-member、non-friend函数。

2、写成member函数引发的问题

写成member函数形式如下:

class Rational{   public:    Rational(int numerator = 0,int denominator = 1) : x(numerator), y(denominator){   }    //构造函数不为explicit,允许int-to-Rational隐式转换     int numerator() const; //分子    int denominator() const; //分母    const Rational operator*(const Rational& rhs) const //有理数的乘法    {       	return Rational(x*rhs.x, y*rhs.y);    }private:	int x;	int y;};
(1)此时执行普通的运算是没问题的:
Rational oneEight(1, 8);	Rational oneHalf(1, 2);	Rational result = onHalf*oneEight;	result = result*oneEight;
(2)但是执行混合运算时却会发生错误:
result = oneHalf * 2; //很好	result = 2 * oneHalf; //错误
(3)第二句为什么会引发错误呢?
result = oneHalf.operator*(2);	result = 2.operator*(oneHalf);

首先,oneHalf确实可以作为operator* 的参数,但是问题是编译器找不到2 所属的类的operator* 的成员方法,所以就会报错。

(4)第一句为什么能够通过编译呢?

答案就是发生了隐式转换,首先oneHalf有成员方法operator* ,它需要一个参数Rational 类对象,但是传递的参数确是一个int 型的2,于是编译器开始查找Rational 的类型转换构造函数,看能否把2转换成Rational 类对象,它找到了,于是产生了一个临时的对象,然后将这个对象传递给了operator*,因此编译通过。这便是所谓的隐式转换。

(5)为什么第二句的参数2不能发生隐式转换呢?

只有当参数被列于参数列内,而不是隐喻参数this时,这个参数才是隐式转换的合格参与者。隐喻参数this不是隐式转换的合格参与者。

如果将类型转换构造函数写成non-explicit 的话,就达成了二者的一致性,即:都不能通过编译。

显然我们的有理数类是应该支持混合运算的,那么我们的设计就需要改进。

将operator* 写成non-member、non-friend函数

class Rational{   public:	Rational(int numerator = 0, int denominator = 1) : x(numerator), y(denominator) {   }	//构造函数不为explicit,允许int-to-Rational隐式转换 	int numerator() const //分子	{   		return x;	}	int denominator() const //分母	{   		return y;	}private:	int x;	int y;};const Rational operator*(const Rational& lhs, const Rational& rhs){   	return Rational(lhs.numerator()*rhs.numerator(), lhs.denominator()*rhs.denominator());}

3、为什么写成non-friend的,而不写成friend的?

(1)误解

首先,澄清一个误解。member函数的反面不一定是non-member、friend的。而是non-member、non-friend的。

(2)原则

无论何时,我们都应该避免non-member函数写成friend的。当然,也有必须写成friend的场景,friend是有用的。

4、本条款注意点

当进入泛型编程时,本条款可能就不太适用了。

上一篇:条款25: 考虑写出一个不抛异常的swap函数
下一篇:条款23:宁以non-member、non-friend替换member函数

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2025年04月16日 15时49分00秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

CentOS系列:【Linux】CentOS7操作系统安装nginx实战(多种方法,超详细) 2023-01-23
CSDN----Markdown编辑器 2023-01-23
Docker容器进入的4种方式(推荐最后一种) 2023-01-23
Docker部署postgresql-11以及主从配置 2023-01-23
EnvironmentNotWritableError: The current user does not have write permissions to the target environm 2023-01-23
Golang起步篇(Windows、Linux、mac三种系统安装配置go环境以及IDE推荐以及入门语法详细释义) 2023-01-23
Hyper-V系列:windows11开启系统自带安卓虚拟机并安装apk包 2023-01-23
Hyper-V系列:微软官方文章 2023-01-23
idea打war包的两种方式 2023-01-23
Java系列:【注释模板】IDEA中JAVA类、方法注释模板教程 2023-01-23
JS系列(仅供参考):【浏览器编程】浏览器F12调试工具面板详解和JavaScript添加断点 2023-01-23
Kali 更换源(超详细,附国内优质镜像源地址) 2023-01-23
kali安装docker(亲测有效) 2023-01-23
Linux系列:Linux目录分析:[/] + [/usr] + [/usr/local] + [/usr/local/app-name]、Linux最全环境配置 + 动态库/静态库配置 2023-01-23
Linux系列:ubuntu各版本之间的区别以及Ubuntu、kubuntu、xUbuntu、lubuntu等版本区别及界面样式 2023-01-23
mysql系列:远程连接MySQL错误“plugin caching_sha2_password could not be loaded”的解决办法 2023-01-23
Nessus扫描结果出现在TE.IO或者ES容器结果查看问题解决方案 2023-01-23
Nmap渗透测试指南之探索网络 2023-01-23
Nmap渗透测试指南之防火墙/IDS逃逸、信息搜集 2023-01-23
Nmap端口服务 之 CentOS7 关于启动Apache(httpd)服务、telnet服务、smtp服务、ftp服务、sftp服务、snmp服务 2023-01-23