07-友元
11.3 友元
您知道,C++控制对类对象私有部分的访问。通常,公有类方法提供唯一的访问途径,但是有时候这种限制太严格,以致于不适合特定的编程问题。在这种情况下,C++提供了另外一种形式的访问权限:友元。友元有3种:
- 友元函数;
- 友元类;
- 友元成员函数。
通过让函数成为类的友元,可以赋予该函数与类的成员函数相同的访问权限。下面介绍友元函数,其他两种友元将在第15章介绍。
介绍如何成为友元前,先介绍为何需要友元。在为类重载二元运算符时(带两个参数的运算符)常常需要友元。将Time对象乘以实数就属于这种情况,下面来看看。
在前面的Time类示例中,重载的乘法运算符与其他两种重载运算符的差别在于,它使用了两种不同的类型。也就是说,加法和减法运算符都结合两个Time值,而乘法运算符将一个Time值与一个double值结合在一起。这限制了该运算符的使用方式。记住,左侧的操作数是调用对象。也就是说,下面的语句:
A = B * 2.75;
将被转换为下面的成员函数调用:
A = B.operator*(2.75);
但下面的语句又如何呢?
A = 2.75 * B; // cannot correspond to a member function
从概念上说,2.75 B应与B 2.75相同,但第一个表达式不对应于成员函数,因为2.75不是Time类型的对象。记住,左侧的操作数应是调用对象,但2.75不是对象。因此,编译器不能使用成员函数调用来替换该表达式。
解决这个难题的一种方式是,告知每个人(包括程序员自己),只能按B 2.75这种格式编写,不能写成2.75 B。这是一种对服务器友好-客户警惕的(server-friendly, client-beware)解决方案,与OOP无关。
然而,还有另一种解决方式——非成员函数(记住,大多数运算符都可以通过成员或非成员函数来重载)。非成员函数不是由对象调用的,它使用的所有值(包括对象)都是显式参数。这样,编译器能够将下面的表达式:
A = 2.75 * B; // cannot correspond to a member function
与下面的非成员函数调用匹配:
A = operator*(2.75, B);
该函数的原型如下:
Time operator*(double m, const Time & t);
对于非成员重载运算符函数来说,运算符表达式左边的操作数对应于运算符函数的第一个参数,运算符表达式右边的操作数对应于运算符函数的第二个参数。而原来的成员函数则按相反的顺序处理操作数,也就是说,double值乘以Time值。
使用非成员函数可以按所需的顺序获得操作数(先是double,然后是Time),但引发了一个新问题:非成员函数不能直接访问类的私有数据,至少常规非成员函数不能访问。然而,有一类特殊的非成员函数可以访问类的私有成员,它们被称为友元函数。