在了解友元之前,我们需要知道为什么需要友元。
在为类重载二元运算符时常常需要友元。
假设Time是我们自己定义的关于时间的类,现在要重载乘法运算符,使得Time值可以与一个double值相乘。但是如果用成员函数重载乘法运算符,左侧的操作数是调用对象,也就是说,下面的语句:
A = B * 2.75;
将被转换成下面的成员函数调用:
A = B.operator * (2.75);
但是下面的语句又会怎么样呢?
A = 2.75*B;
因为2.75不是Time类型的对象,因此,编译器不能使用成员函数来替换该表达式。
这时候,我们还有另外一种方式----非成员函数。非成员函数不是由对象调用的,它使用的所有值(包括对象)都是显式参数。这样子,编译器就能够将下面的表达式:
A = 2.75*B;
与下面的非成员函数调用匹配:
A = operator * (2.75, B);
该非成员函数的原型如下:
Time operator * (double m, const Time& t);
但是这个时候又出现了新的问题,常规的非成员函数不能直接访问类的私有成员。有一类特殊的非成员函数可以访问类的私有成员,它们被成为友元函数。
创建友元函数的第一步是将原型放在类声明中,并且在原型声明前加上关键字friend:
class Time
{
private:
int hours;
int minutes;
public:
Time();
Time(int h, int m = 0);
void AddMin(int m);
void AddHr(int h);
void Reset(int h = 0, int m = 0);
Time operator+(const Time & t) const;
Time operator-(const Time & t) const;
Time operator*(double n) const;
void Show() const;
friend Time operator * (double m, const Time& t);
};
第二步是编写函数定义。因为它不是成员函数,所以不要使用Time::限定符。另外,不要在定义中使用friend, 定义应该如下:
Time operator * (double mult, const Time& t)
{
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);
总之,类的友元函数是非成员函数,其访问权限与成员函数相同。