创建友元函数的第一步是将其原型放在类声明中,并在原型声明前加上关键字 friend:
friend Time operator*(double m,const Time Time& t); //goes in class declaration
该原型意味着下面两点:
- 虽然 operator*()函数是在类声明中声明的,但它不是成员函数,因此不能使用成员运算符来调用;
- 虽然 operator*()函数不是成员函数,但它与成员函数的访问权限相同。
第二步是编写函数定义。因为它不是成员函数,所以不要使用Time:限定符。另外,不要在定义中使用关键字 friend,定义应该如下:
Time operator*(double mult,const Time& t) //friend not used in definition{Time result;long totalminutes=t.hours * mult * 60+t.minutes * mult;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和 thours,所以它必须是友元。这个版本将Time对象t作为一个整体使用,让成员函数来处理私有值,因此不必是友元。然而,将该版本作为友元也是一个好主意。最重要的是,它将该作为正式类接口的组成部分。其次,如果以后发现需要函数直接访问私有数据,则只要修改函数定义即可,而不必修改类原型.
提示: 如果要为类重载运算符,并将非类的项作为其第一个操作数,则可以用友元函数来反转操作数的顺序。
