当前位置: 首页 > news >正文

Item24--若所有参数皆需类型转换,请为此采用 non-member 函数

1. 核心场景:混合算术运算

为了讲解这个条款,我们依然使用经典的 有理数类 (Rational),并希望支持整数隐式转换为有理数(例如 5 变成 5/1)。

前提条件:你的构造函数不能explicit 的(允许隐式转换)。

class Rational {
public:// 构造函数不带 explicit,允许 int -> Rational 的隐式转换Rational(int numerator = 0, int denominator = 1); int numerator() const;int denominator() const;
};

我们现在的目标是支持乘法:Rational * int,以及 int * Rational


2. 错误的尝试:作为 Member Function

按照面向对象的直觉,我们首先把它写成成员函数:

class Rational {
public:...// 成员函数版乘法const Rational operator*(const Rational& rhs) const;
};

测试 Case 1:正常情况

Rational oneHalf(1, 2);
Rational result = oneHalf * oneHalf; // ✅ 没问题

等价于:oneHalf.operator*(oneHalf)

测试 Case 2:混合运算(对象在左,Int 在右)

result = oneHalf * 2; // ✅ 没问题!

发生了什么?

  1. 编译器看:oneHalf.operator*(2)
  2. 参数类型不匹配:函数需要 Rational,但你给了 int
  3. 编译器发现:构造函数 Rational(int) 可以把 2 变成 Rational(2)
  4. 隐式转换发生:调用成功。

测试 Case 3:混合运算(Int 在左,对象在右)

result = 2 * oneHalf; // ❌ 编译失败!

为什么失败?

  1. 这行代码试图执行:2.operator*(oneHalf)
  2. 整数 2 是内置类型,它没有成员函数,当然也就没有 operator*
  3. 编译器不会尝试把左侧的 2 转换成 Rational 来调用成员函数

核心规则:只有当参数列在参数列表(Parameter List)中时,隐式类型转换才会发生。 对于成员函数,this 指针(即调用者本身,操作符左侧的对象)不是函数参数,所以它绝不会发生隐式转换。


3. 正确的解法:Non-Member Function

为了让乘法交换律成立(即 A * BB * A 都要支持),我们需要让操作符左侧和右侧的变量地位平等,都能够享受“隐式类型转换”的待遇。

这就要求:两个都要是函数的参数。 显然,只有 Non-member function 能做到这一点。

// 定义在类外面
const Rational operator*(const Rational& lhs, const Rational& rhs) {return Rational(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator());
}

再次测试 Case 3

result = 2 * oneHalf; // ✅ 现在成功了!

发生了什么?

  1. 编译器寻找 operator*(2, oneHalf)
  2. 发现我们定义的全局函数接受两个 Rational
  3. 编译器非常勤快:
    • 把左边的 2 隐式转换为 Rational(2)
    • 右边已经是 Rational
  4. 函数调用成功!

4. 这里的 Friend 问题

在 Item 23 中,我们刚学过“尽量不要用 Friend”。那么在 Item 24 中,这个非成员的 operator* 应该是 Friend 吗?

这取决于 Public 接口是否足够。

情况 A:Public 接口足够(推荐)

如果 Rational 类提供了 numerator()denominator() 的 Public 访问函数(如上面的例子),那么 operator* 完全不需要是 Friend。 它可以利用 Public 接口完成任务。符合 Item 23 的精神:Non-member Non-friend

情况 B:Public 接口不足(不得不 Friend)

如果 Rational 没有公开分母和分子的访问器,或者为了极致的性能(省去函数调用开销,虽然内联能解决),你必须直接访问私有数据 nd。 这时,你不得不把 operator* 声明为 Friend

总结:Item 24 的核心是“必须是 Non-member”。至于是不是 Friend,看具体情况,能不是就不是。


5. 总结 (Summary)

  1. 问题:如果你希望一个函数的所有参数(尤其是左边的那个)都能支持隐式类型转换
  2. 限制:Member function 的 this 指针(左侧对象)无法进行隐式转换。
  3. 解决方案:必须将该函数定义为 Non-member function
  4. 典型场景:算术运算符重载(operator*, operator+ 等)涉及混合运算(int * Object)时。
http://www.rkmt.cn/news/127925.html

相关文章:

  • 基于java的SpringBoot/SSM+Vue+uniapp的赛车爱好者交流平台的详细设计和实现(源码+lw+部署文档+讲解等)
  • Item25--考虑写出一个不抛异常的 swap 函数
  • 3562. 折扣价交易股票的最大利润
  • Item15--在资源管理类中提供对原始资源的访问
  • Item21--必须返回对象时,别妄想返回其 reference
  • 1985-2024年中国绿色专利数据库(绿色技术专利分类)
  • Item16--`new` 与 `delete` 的对应规则
  • 预见2026:家居新品首秀平台选择战略——五大核心展会深度评估与推荐 - 匠子网络
  • 国外软件,安装即时专业版!
  • 个人投资者的落地路径:从“说人话,做量化”到实盘前的三道关
  • item13--使用对象管理资源
  • sub_match
  • python django flask酒店客房管理系统数据可视化分析系统_gq8885n3--论文md5
  • python django flask鹿幸公司员工食堂在线点餐餐饮餐桌预约管理系统的设计与实现_utcnqqs0--论文
  • error_code
  • 好用的大型牛场水滴粉碎机技术强的
  • function bind
  • Item6--若不想使用编译器自动生成的函数,就该明确拒绝
  • 我发现LLM解析基因数据优化抗癌药剂量,患者副作用直降40%
  • 线程(1)
  • 日记12.16
  • 信息与关系:涌现的三大核心原则
  • 论文AIGC查重率高怎么办?6个降AI率工具和技巧,AI率从100%降到3%! - 还在做实验的师兄
  • 离散化遍历
  • 28
  • 人工智能与大数据:智能决策的新时代 - 教程
  • 花边服饰银发红眸者山间近景
  • 互联网大厂Java面试:从Spring Boot到微服务架构的技术探讨
  • 性价比高的老房换新实用门窗品牌精选指南排名
  • 12月20日总结 - 作业----