
序言
您知道,C++控制对类对象私有部分的访问。通常,公有类方法提供唯一的访问途径,但是有时候这种限制太严格,以致于不适合特定的编程问题。
在这种情况下,C++提供了另外一种形式的访问权限:友元。友元有3种:
- 友元函数;
- 友元类;
- 友元成员函数
通过让函数成为类的友元,可以赋予该函数与类的成员函数相同的访问权限。下面介绍友元函数。
为什么要使用友元函数
在前面的Time类示例中,重载的乘法运算符与其他两种重载运算符的差别在于,它使用了两种不同的类型。也就是说,加法和减法运算符都结合两个 Time 值,而乘法运算符将一个 Time 值与一个 double值结合在一起。这限制了该运算符的使用方式。记住,左侧的操作数是调用对象。也就是说,下面的语句:
A = B * 2.75;
将被转换为下面的成员函数调用
A = B.operator*(2.75);
但下面的语句又如何呢?
A = 2.75 * B;
从概念上说,2.75 * B应与B *2.75相同,但第一个表达式不对应于成员函数,因为2.75不是Time类型的对象。记住,左侧的操作数应是调用对象,但2.75不是对象。因此,编译器不能使用成员函数调用来替换该表达式。
有一种解决方式是使用非成员函数,非成员函数不是由对象调用的,它使用的所有值(包括对象)都是显式参数。这样,编译器能够将下面的表达式:
A = 2.75 * B;
与下面的非成员函数调用匹配:
A = operator*(2.75, B);
该函数的原型如下:
Time operator*(double m, const Time & t)
使用非成员函数可以按所需的顺序获得操作数(先是 double,然后是 Time),但引发了一个新问题:非成员函数不能直接访问类的私有数据,至少常规非成员函数不能访问。
然而,有一类特殊的非成员函数可以访问类的私有成员,它们被称为友元函数。
创建友元
创建友元函数的第一步是将其原型放在类声明中,并在原型声明前加上关键字friend:
friend Time operator*(double m, const Time & t)
该原型意味着下面两点:
虽然operator*( )函数是在类声明中声明的,但它不是成员函数,因此不能使用成员运算符来调用;
虽然operator*( )函数不是成员函数,但它与成员函数的访问权限相同。
第二步是编写函数定义。因为它不是成员函数,所以不要使用 Time::限定符。另外,不要在定义中使用关键字friend,定义应该如下:
Time operator*(double m, const Time & t)
{
Time result;
long totalminutes = t.hours * mult * 60 + t.minutes * mult;
result.hours = totalminutes / 60;
result.minutes = totalminutes %
}
有了上述声明和定义后,下面的语句:
A = 2.75 * B;
将转换为如下语句,从而调用刚才定义的非成员友元函数:
总之,类的友元函数是非成员函数,其访问权限与成员函数相同。
友元没有违背OOP
乍一看,您可能会认为友元违反了OOP数据隐藏的原则,因为友元机制允许非成员函数访问私有数据。然而,这个观点太片面了。相反,应将友元函数看作类的扩展接口的组成部分。只有类声明可以决定哪一个函数是友元,因此类声明仍然控制了哪些函数可以访问私有数据。
总之,类方法和友元只是表达类接口的两种不同机制。
总结
暂时没有
