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

08-创建友元

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

11.3.1 创建友元

创建友元函数的第一步是将其原型放在类声明中,并在原型声明前加上关键字friend:

friend Time operator*(double m, const Time & t); // goes in class declaration

该原型意味着下面两点:

  • 虽然operator *()函数是在类声明中声明的,但它不是成员函数,因此不能使用成员运算符来调用;
  • 虽然operator *()函数不是成员函数,但它与成员函数的访问权限相同。

第二步是编写函数定义。因为它不是成员函数,所以不要使用Time::限定符。另外,不要在定义中使用关键字friend,定义应该如下:

Time operator*(double m, const Time & t) // friend not used in definition
{
    Time result;
    long totalminutes = t.hours * m * 60 +t. minutes * m;
    result.hours = totalminutes / 60;
    result.minutes = totalminutes % 60;
    return result;
}

有了上述声明和定义后,下面的语句:

A = 2.75 * B;

将转换为如下语句,从而调用刚才定义的非成员友元函数:

A = operator*(2.75, B);

总之,类的友元函数是非成员函数,其访问权限与成员函数相同。

友元是否有悖于OOP

乍一看,您可能会认为友元违反了OOP数据隐藏的原则,因为友元机制允许非成员函数访问私有数据。然而,这个观点太片面了。相反,应将友元函数看作类的扩展接口的组成部分。例如,从概念上看,double乘以Time和Time乘以double是完全相同的。也就是说,前一个要求有友元函数,后一个使用成员函数,这是C++句法的结果,而不是概念上的差别。通过使用友元函数和类方法,可以用同一个用户接口表达这两种操作。另外请记住,只有类声明可以决定哪一个函数是友元,因此类声明仍然控制了哪些函数可以访问私有数据。总之,类方法和友元只是表达类接口的两种不同机制。

实际上,按下面的方式对定义进行修改(交换乘法操作数的顺序),可以将这个友元函数编写为非友元函数:

Time operator*(double m, const Time & t)
{
    return t * m;   // use t.operator*(m)
}

原来的版本显式地访问t.minutes和t.hours,所以它必须是友元。这个版本将Time对象t作为一个整体使用,让成员函数来处理私有值,因此不必是友元。然而,将该版本作为友元也是一个好主意。最重要的是,它将作为正式类接口的组成部分。其次,如果以后发现需要函数直接访问私有数据,则只要修改函数定义即可,而不必修改类原型。

提示: 如果要为类重载运算符,并将非类的项作为其第一个操作数,则可以用友元函数来反转操作数的顺序。