7.4 友元函数与友元类
类的友元函数(friendfunetlon)在类范围之外定义,但有权访问类的 private(和第9章“继承”介绍的受保护)成员。函数或整个类都可以声明为另一个类的友元。
利用友元函数能提高性能,这里将介绍一个友元函数的例子。本书后面要用友元函数通过类对象和生成迭代类来重载运算符。迭代类对象用于连续选择项目或对容器类(见7.9节)对象中的项目进行操作。容器类对象能够存放项目。成员函数无法进行某些操作时可以使用友元函数(见第8章 运算符重载)。
要将函数声明为类的友元,就要在类定义中的函数原型前面加上friend关键字。要将类ClassTwo声明为类ClassOne的友元,在类 ClassOne 的定义中声明如下:
friend class classTwo;
软件工程视点 7.9
尽管类定义中有友元函数的原型,但友元仍然不是成员函数。
软件工程视点 7.10
private、protected 和 public 的成员访问符号与友元关系的声明无关,因此友元关系声明可以放在类定义中的任何地方。
编程技巧 7.2
将类中所有友元关系的声明放在类的首部之后,不要在其前面加上任何成员访问说明符。
友元关系是 给予 的,而不是 索取 的,即要让B成为A的友元,A要显式声明B为自己的友元。此外,友元关系既不对称也不能传递,例如.如果A是D的友元,B是C的友元,并不能说B就是A的友元(不对称)、c就是B的友元或A就是C的友元(不传递)。
软件工程视点 7.11
OOP 组织中的有些人认为友元关系会破坏信息隐藏和降低面向对象设计方法的价值。
图 7.5 演示了声明与使用友元函数 setX 来设置 Count 类的 private 数据成员 x。注意,类声明中(习惯上)首先是友元声明,放在 public 成员函数的声明之前。图 7.6 的程序演示了调用非友元函数 cannotSetX 修改 private 数据成员x时编译器产生的消息。图 7.5 和 7.6 介绍了使用友元函数的“结构”,今后各章会介绍使用友元函数的实际例子。
1 // Fig. 7.5:fig0705.cpp
2 // Friends can access private members of a class.
3 #include <iostream.h>
4
5 // Modified Count class
6 class Count {
7 friend void setX( Count &,int ); // friend declaration
8 public:
9 Count() { x = 0; } // constructor
10 void print()const {cout << x << endl; } // output
11 private:
12 int x; // data member
13 };
14
15 // Can modify private data of Count because
16 // setX is declared as a friend function of Count
17 void setX( Count &c, int val )
18 {
19 c.x = val; // legal: setX is a friend of Count
2O }
21
22 int main()
23 {
24 Count counter;
25
26 cout << "counter.x after instantiation: ";
27 counter.print();
28 cout << "counter.x after call to setx friend function: ";
29 setX( counter, 8 ); // set x with a friend
30 counter.print();
31 return 0;
32 }
输出结果:
counter.x after intantiation: 0
counter.x after call to setX friend function: 8
图 7.5 友元可以访问类的 private 成员
注意第17行中函数 setX 是C语言式的独立函数,而不是Count类的成员函数,为此,对counter对象调用 setX 时,我们用第29行的语句:
setX(counter,8 ); // set x with a friend
来提取 counter 参数而不是用句柄(如对象名)调用如下函数:
counter.setX(s);
软件工程视点 7.12
由于 C++ 是个混合语言,经常在一个程序中并行采用两种函数调用,类C语言的调用将基本数据或对象传递给函数,而 C++ 调用将函数(或消息)传递给对象。
1 // Fig. 7.6: fig07_O6.cpp
2 // Non-friend/non-member functions cannot access
3 // private data of a class.
4 #include <iostream.h>
5
6 // Modified Count class
7 class Count {
8 public:
9 Count() { x = 0; } // constructor
10 void print() const { cout << x << endl; } // output
11 private:
12 int x; // data member
13 };
14
15 // Function tries to modify private data of Count,
16 // but cannot because it is not a friend of Count.
17 void cannotSetX( Count &c, int val )
16 {
19 c.x = val; // ERROR: 'Count::x' is not accessible
20 }
21
22 int main()
23 {
24 Count counter;
25
26 cannotSetX( counter, 3 ); // cannotSetX is not a friend
27 return 0;
28 }
输出结果:
Compiling...
Fig07 06.cpp
Fig07_06.cpp(19) : error: 'x' :
Cannot access private member declared in class 'Count'
图 7.6 非友元/非成员函数不能访问类的 Private 成员
可以指定重载函数为类的友元。每个重载函数如果要作为友元,就要在类定义中显式声明为类的友元。