当前位置: 首页 > 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.jsqmd.com/news/115974/

相关文章:

  • 基于java的SpringBoot/SSM+Vue+uniapp的赛车爱好者交流平台的详细设计和实现(源码+lw+部署文档+讲解等)
  • Item18--让接口容易被正确使用,不易被误用
  • 算法日记专题:位运算II( 只出现一次的数字I II III 面试题:消失的两个数字 比特位计数)
  • PyTorch - 指南
  • 【2025红黑榜】10款常见降AI率工具大汇总(含不限次数免费降AI版本)
  • Item25--考虑写出一个不抛异常的 swap 函数
  • Item25--考虑写出一个不抛异常的 swap 函数
  • 3562. 折扣价交易股票的最大利润
  • 【2025终极测评】10款常见降AI率工具大汇总(含0元免费降AI版本)
  • 算法日记专题:位运算I(汉明距离I II 面试题:判断是不是唯一的字符 丢失的数字 两个整数相加)
  • Item21--必须返回对象时,别妄想返回其 reference
  • Item15--在资源管理类中提供对原始资源的访问
  • 1985-2024年中国绿色专利数据库(绿色技术专利分类)
  • Item22--将成员变量声明为 private
  • Item16--`new` 与 `delete` 的对应规则
  • 3777. 使子字符串变交替的最少删除次数
  • item11--在 operator= 中处理“自我赋值
  • 预见2026:家居新品首秀平台选择战略——五大核心展会深度评估与推荐 - 匠子网络
  • Item20--宁以 pass-by-reference-to-const 替换 pass-by-value
  • 研究生必备!8个免费AI论文工具,半天生成5000字问卷论文还有高信度数据
  • Item20--宁以 pass-by-reference-to-const 替换 pass-by-value
  • Item17--以独立语句将 `new` 到的对象置入智能指针
  • Item17--以独立语句将 `new` 到的对象置入智能指针
  • 3433. 统计用户被提及情况
  • Item19--设计 class 犹如设计 type
  • 国外软件,安装即时专业版!
  • Item19--设计 class 犹如设计 type
  • basic_regex
  • c++狼人杀
  • 宠物识别丨基于弱监督学习的宠物视频内容自动标注技术实践 - 指南