当前位置:嗨网首页>书籍在线阅读

18-转换函数和友元函数

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

11.6.2 转换函数和友元函数

下面为Stonewt类重载加法运算符。在讨论Time类时指出过,可以使用成员函数或友元函数来重载加法。(出于简化的目的,我们假设没有定义operator double()转换函数。)可以使用下面的成员函数实现加法:

Stonewt Stonewt::operator+(const Stonewt & st) const
{
    double pds = pounds + st.pounds;
    Stonewt sum(pds);
    return sum;
}

也可以将加法作为友元函数来实现,如下所示:

Stonewt operator+(const Stonewt & st1, const Stonewt & st2)
{
    double pds = st1.pounds + st2.pounds;
    Stonewt sum(pds);
    return sum;
}

别忘了,可以提供方法定义或友元函数定义,但不能都提供。上面任何一种格式都允许这样做:

Stonewt jennySt(9, 12);
Stonewt bennySt(12, 8);
Stonewt total;
total = jennySt + bennySt;

另外,如果提供了Stonewt(double)构造函数,则也可以这样做:

Stonewt jennySt(9, 12);
double kennyD = 176.0;
Stonewt total;
total = jennySt + kennyD;

但只有友元函数才允许这样做:

Stonewt jennySt(9, 12);
double pennyD = 146.0;
Stonewt total;
total = pennyD + jennySt;

为了解其中的原因,将每一种加法都转换为相应的函数调用。首先:

total = jennySt + bennySt;

被转换为:

total = jennySt.operator+(bennySt); // member function

或:

total = operator+(jennySt, bennySt); // friend function

上述两种转换中,实参的类型都和形参匹配。另外,成员函数是通过Stonewt对象调用的。

其次:

total = jennySt + kennyD;

被转换为:

total = jennySt.operator+(kennyD); // member function

或:

total = operator+(jennySt, kennyD); // friend function

同样,成员函数也是通过Stonewt对象调用的。这一次,每个调用中都有一个参数(kennyD)是double类型的,因此将调用Stonewt(double)构造函数,将该参数转换为Stonewt对象。

另外,在这种情况下,如果定义了operator double()成员函数,将造成混乱,因为该函数将提供另一种解释方式。编译器不是将kennyD转换为Stonewt并执行Stonewt加法,而是将jennySt转换为double并执行double加法。过多的转换函数将导致二义性。

最后:

total = pennyD + jennySt;

被转换为:

total = operator+(pennyD, jennySt); // friend function

其中,两个参数都是double类型,因此将调用构造函数Stonewt(double),将它们转换为Stonewt对象。

然而,不能调用成员函数将jennySt和peenyD相加。将加法语法转换为函数调用将类似于下面这样:

total = pennyD.operator+(jennySt); // not meaningful

这没有意义,因为只有类对象才可以调用成员函数。C++不会试图将pennyD转换为Stonewt对象。将对成员函数参数进行转换,而不是调用成员函数的对象。

这里的经验是,将加法定义为友元可以让程序更容易适应自动类型转换。原因在于,两个操作数都成为函数参数,因此与函数原型匹配。

实现加法时的选择

要将double量和Stonewt量相加,有两种选择。第一种方法是(刚介绍过)将下面的函数定义为友元函数,让Stonewt(double)构造函数将double类型的参数转换为Stonewt类型的参数:

operator+(const Stonewt &, const Stonewt &)

第二种方法是,将加法运算符重载为一个显式使用double类型参数的函数:

Stonewt operator+(double x); // member function
friend Stonewt operator+(double x, Stonewt & s);

这样,下面的语句将与成员函数operator + (double x)完全匹配:

total = jennySt + kennyD; // Stonewt + double

而下面的语句将与友元函数operator + (double x, Stonewt &s)完全匹配:

total = pennyD + jennySt; // double + Stonewt

前面对Vector乘法做了类似的处理。

每一种方法都有其优点。第一种方法(依赖于隐式转换)使程序更简短,因为定义的函数较少。这也意味程序员需要完成的工作较少,出错的机会较小。这种方法的缺点是,每次需要转换时,都将调用转换构造函数,这增加时间和内存开销。第二种方法(增加一个显式地匹配类型的函数)则正好相反。它使程序较长,程序员需要完成的工作更多,但运行速度较快。

如果程序经常需要将double值与Stonewt对象相加,则重载加法更合适;如果程序只是偶尔使用这种加法,则依赖于自动转换更简单,但为了更保险,可以使用显式转换。