|
一般情况下,访问类的私有部分,唯一的访问途径是通过共有类方法。如果只是这样,那么限制就太为严格,以致于不适合特定的编程问题,这时就产生了另外一种形式的访问权限:友元。友元有 3 种:友员函数,友元类,友元成员函数。
那我们在什么时候会用到友元呢?常见的情况是在为类重载二元运算符时(带两个参数的运算符)。
假如有一个类 Time,其中 A 和 B 都是该类的对象,我们重载了该类中的乘法运算符,那么可有:
首先需要注意的是,运算符左侧的操作数是调用对象,也就是说,B 调用了重载的乘法运算符 * ,因此上面的语句可以转换为下面的成员函数调用:
从概念上来讲,2.75 * B 和 B * 2.75 是一样的。但在 C++ 编程里,这就有了区别:
由于 2.75 不是类型对象,所以编译器不能使用成员函数调用替换该表达式(记住,左侧的操作数应该是调用对象!)。解决问题的办法是,告诉大家只能按照 B*2.75 这种格式来编写。然而,还有另一种解决方式 ---- 非成员函数(因为成员函数是需要对象来调用的,而左侧若不是对象,那么就没法使用成员函数,因此只能是非成员函数。需要注意的是,大多数运算符都可以通过成员或非成员函数来重载)。非成员函数并不由对象调用,它使用的所有的值(包括对象),都是显式参数。这样,编译器能够将表达式 A = 2.75 * B; 与 A = operator *(double m, const Time & t); 匹配起来。
该非成员函数的原型声明如下:
Time operator * (double m, const Time &t);
对于非成员重载运算符函数来说,运算符表达式左边的操作数对应于运算符函数的第一个参数,表达式右边操作数对应于运算符函数的第二个参数。
虽然使用非成员函数可以按所需的顺序获得操作数(如上是先 double ,然后是 Time),但却引发了另一个新的问题:非成员函数不能直接访问类的私有数据,至少常规非成员函数不能访问。于是,定义一类特殊的可以访问类的私有成员的非成员函数就可以直接这个问题,这些函数被称为友员函数。
友元的概念是否有悖于 OOP ?
由于友元允许非成员函数访问私有数据,乍一想,可能会认为它违反了 OOP 的数据隐藏规则,其实不然。相反,应该将友元看做是类的扩展接口的组成部分。例如,从概念上看,double * Time 和 Time * double 是完全相同的。也就是说,前一个要求有友员函数,后一个使用成员函数,这是 C++ 句法的结果,而不是概念上的差别。通过使用友元函数和类方法,可以用同一个用户接口表达这两种操作。另外需要记住的是:只有类声明可以决定哪一个函数是友元,因此类声明仍然控制了哪些函数可以访问私有数据。可以打个比方,主人(类)一般只允许客人参观大厅、厨房这两个地方,但不允许参观其寝室,书房,然后特别要好的朋友(友元)除外,亦是参不参观寝室书房,完全由主人决定。总之,类方法和友元只是表达类接口的两种不同机制。
提示:如果要为类重载运算符,并将非类的项作为其第一个操作数,就可以用友元函数来翻转操作数的顺序。
|
|