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

令class支持隐式类型转换通常是个糟糕的主意。

这条规则有其例外,最常见的例外是在建立数值类型时。

例,假设你设计一个class用来表现有理数,则允许整数“隐式转换”为有理数就很合理。

class Rational{
public:
    Rational(int numerator=0,int denominator=1);//允许隐式类型转换
    int numerator() const;//分子的访问函数
    int denominator() const;//分母的访问函数
};

将operator*写成Rational成员函数:

class Rational{
public:
    //...
    const Rational operaotr*(const Rational& lhs,const Rational& rhs);
};

这个设计能让你将两个有理数进行相乘:

Rational oneEighth(1,8);
Rational oneHalf(1,2);
Rational result=oneHalf*oneEnglish;
result=result*oneEighth;

但当你尝试混合式算术时,你发现只有一半行得通:

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

为什么?

当你以对应的函数形式重写上述两个式子,就能发现问题的所在:

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

因为oneHalf是一个内含operator*函数的class对象,所以编译器调用该函数。但2没有相应的class,也就没有operator*成员函数,因此上述第二行代码是错误的。

那为什么对于oneHalf*2,这个表达式不是错误的。其第二个参数是整数2,但Rational::operator*需要的实参却是个Rational对象。这里发生了什么?为什么第一个可以,第二个不可以?

这里发生了隐式类型转换(implicit type conversion),编译器知道你正在传递一个int,但函数需要的是Rational,但它知道知道调用Rational构造韩素华并赋予你所提供的int,就可以变出一个适当的Rational来。因此,此调用动作在编译器眼中相当于:

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

注意,上述转换只是因为构造函数是non-explicit的,若Rational构造函数时explicit,则以下语句没有一个可以通过编译:

result=oneHalf*2;//错误,无法将2转换为一个Rational
result=2*oneHalf;//错误

但为什么即使Rational构造函数不是explicit,仍然只有一个可通过编译,另一个不可以。

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

基于上述讨论,让operator*成为一个non-member函数,便允许编译器在每一个实参身上执行隐式类型转换:

class Ratioanl{
    //...
};
const Rational operaotr*(const Rational& lhs,const Rational& rhs)
{
    return 
    Rational(lhs.numerator()*rhs.numerator(),lhs.denominator()*rhs.denominator());
}
int main()
{
    Rational oneFourth(1,4);
    Rational result;
    result=oneFourth*2;//正确
    result=2*oneFourth;//正确
}

还有一点需要考虑,operator*是否应该成为Rational class的一个friend函数?

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

无论何时若你可以避免friend函数就该避免,不能够只因函数不该成为member,就自动让它成为friend。

总结

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 1. max(a, b) - 返回a和b中较大的值 2. isdigit(c) - 判断一个字符是否是数字 3. strlen(str) - 返回一个字符串的长度 4. pow(base, exp) - 返回base的exp次方 5. srand(seed) - 根据seed设定随机数生成器的种子 6. tolower(c) - 将一个字符转换为小写形式 7. floor(x) - 返回不大于x的最大整数 8. ceil(x) - 返回不小于x的最小整数 9. strchr(str, c) - 在一个字符串中查找第一个匹配c的字符,并返回它的位置 10. strcmp(str1, str2) - 比较两个字符串的大小,如果相等则返回0 ### 回答2: 非成员函数是指不隶属于一个类的函数,也称为独立函数或自由函数。非成员函数是在类外部定义的,不与任何特定的类或对象关联。 非成员函数在许多情况下都非常有用。以下是一些使用非成员函数的常见情况: 1. 实用功能:非成员函数常用于实现通用的实用功能,这些功能在整个程序中都可以重复使用。例如,一个计算平方的函数就可以被多个类或对象共享。 2. 参数转换:有时,我们可能要将一个类的对象转换为另一个类的对象,非成员函数可以用于执行这种类型转换。例如,可以定义一个非成员函数将一个字符串转换为整数,然后在程序的任何地方都可以使用它。 3. 操作符重载:非成员函数还可以用于重载操作符。通过使用非成员函数来重载操作符,我们可以在不修改类定义的情况下扩展对操作符的使用方式。例如,可以定义一个非成员函数来重载"+"操作符,使得两个类对象可以相加。 4. 命名空间:非成员函数可以放置在命名空间中,以便将相关的函数组织在一起,并与其他相同名称的函数区分开来。这提高了代码的可读性和可维护性。 总之,非成员函数是与特定类或对象无关的函数。它们可以在多个地方使用,并提供了一种在类外部定义通用功能和操作符重载的方式。非成员函数在编程中非常常见,并且在许多场景下都非常有用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值