Item 24:用非成员函数来支持所有元的类型转换 Effective C++笔记
2015-09-10 05:12
357 查看
Item 24: Declare non-member functions when type conversions should apply to all parameters.
虽然Item 15:资源管理类需要提供对原始资源的访问中提到,最好不要提供隐式的类型转化。
但这条规则也存在特例,比如当我们需要创建数字类型的类时。正如
我们的数字类型也希望能够做到这样方便的接口。 当然这一节讨论的问题不是是否应当提供隐式转换,而是如果运算符的所有“元”都需要隐式转换时,请重载该运算符为友元函数。
通过运算符重载来扩展用户定义类型时,运算符函数可以重载为成员函数,也可以作为友元函数。 但如果作为了成员函数,
仍然拿有理数类作为例子,下面的
我们看下面的运算符调用能否成功:
第一个运算符的调用的成功是很显然的。我们看第二个调用:
当编译器遇到运算符
编译器发现该函数声明(它就是定义在
于是对参数
将
对于第三个调用,编译器仍然首先尝试调用:
再次失败,因为并不存在与
这时候第一个参数也可以进行隐式转换。第三个调用(
只有当运算符的元出现在运算符函数的参数列表时,它才会被隐式类型转换。所以当我们需要运算符的所有“元”都可以被隐式转换时, 应当将运算符声明为非成员函数。 在JavaScript或者C#中,这个规则是不需要的,因为编译器/解释器在这里做了更多的工作。比如JavaScript中
除非注明,本博客文章均为原创,转载请以链接形式标明本文地址: http://harttle.com/2015/08/22/effective-cpp-24.html
虽然Item 15:资源管理类需要提供对原始资源的访问中提到,最好不要提供隐式的类型转化。
但这条规则也存在特例,比如当我们需要创建数字类型的类时。正如
double和
int能够自由地隐式转换一样,
我们的数字类型也希望能够做到这样方便的接口。 当然这一节讨论的问题不是是否应当提供隐式转换,而是如果运算符的所有“元”都需要隐式转换时,请重载该运算符为友元函数。
通过运算符重载来扩展用户定义类型时,运算符函数可以重载为成员函数,也可以作为友元函数。 但如果作为了成员函数,
*this将被作为多元操作符的第一元,这意味着第一元不是重载函数的参数,它不会执行类型转换。
仍然拿有理数类作为例子,下面的
Rational类中,将运算符
*重载为成员函数:
class Rational{ public: Rational(int n = 0, int d = 1); int numerator() const; int denominator() const; const Rational operator*(const Rational& rhs) const; ...
我们看下面的运算符调用能否成功:
Rational oneHalf(1, 2); Rational result = oneHalf * oneHalf; // OK result = oneHalf * 2; // OK result = 2 * oneHalf; // Error
第一个运算符的调用的成功是很显然的。我们看第二个调用:
当编译器遇到运算符
*时,它会首先尝试调用:
result = oneHalf.operator*(2);
编译器发现该函数声明(它就是定义在
Rational类中的方法)存在,
于是对参数
2进行了隐式类型转换(
long->
Rational)。所以第二个调用相当于:
Rational tmp(2); result = oneHalf.operator*(tmp);
将
Rational的构造函数声明为
explicit可以避免上述隐式转换,这样第二个调用也会失败。
对于第三个调用,编译器仍然首先尝试调用:
result = 2.operator*(oneHalf);
2属于基本数据类型,并没有成员函数
operator*。于是编译器再尝试调用非成员函数的运算符:
result = operator*(2, oneHalf);
再次失败,因为并不存在与
operator*(long, Rational)类型兼容的函数声明,所以产生编译错误。 但如果我们提供这样一个非成员函数:
const Rational operator*(const Rational& lhs, const Rational& rhs);
这时候第一个参数也可以进行隐式转换。第三个调用(
result = 2 * oneHalf)便会成功,该表达式相当于:
Rational tmp(2); result = operator*(tmp, oneHalf);
只有当运算符的元出现在运算符函数的参数列表时,它才会被隐式类型转换。所以当我们需要运算符的所有“元”都可以被隐式转换时, 应当将运算符声明为非成员函数。 在JavaScript或者C#中,这个规则是不需要的,因为编译器/解释器在这里做了更多的工作。比如JavaScript中
2.toFixed(3)会被解释为Number(2).toFixed(3)
,该表达式的值为"2.000"`。
除非注明,本博客文章均为原创,转载请以链接形式标明本文地址: http://harttle.com/2015/08/22/effective-cpp-24.html
相关文章推荐
- C++ Primer : : 第十四章 : 重载运算符与类型转换之类型转换运算符和重载匹配
- C++ 一个程序获取另一个程序Edit控件的内容
- C++ <getline及atoi>
- C++/MFC如何启动另一个应用程序并获取其进程 ID
- 三种单例模式的C++实现
- 利用顺序表实现多项式相加
- 利用顺序表实现集合的并运算
- C++ 中switch 使用注意事项
- C++ 设计模式(四)----原型模式
- C++11中的std::function
- c语言通讯录的例子
- C++11中的std::bind
- 使用wxWidgets进行跨平台的C++开发
- 循环缓冲区C++的一种实现
- 万年历(C语言代码实现)
- 指针和C++的基本原理
- 冒泡排序和快速排序算法练习
- PAT 一元多项式的乘法与加法运算(链表 c++版)
- PAT 一元多项式的乘法与加法运算(链表 c++版)
- C++特性