私有成员数据除了可能被别的类访问之外,也可能被别的函数或别的类的部分成员函数访问。为了保证类的封装性,我们可以以函数作为单位,“对外开放”类的私有成员。与声明友元类类似,如果我们想用函数访问链表结点类的私有成员数据,则那些函数必须得到链表结点类的认可。声明友元函数的语句格式为:
friend 返回值类型函数名(参数表);
如果该函数是某个类的成员函数,则语句格式为:
friend 返回值类型类名::函数名(参数表);
需要注意的是,在声明友元成员函数时,可能会牵扯出一系列的类的声明顺序问题。当类的结构本身就比较复杂时,友元的使用可能会使得这个问题愈加突出。
下面我们就用友元函数来输出一个结点的信息:(程序16.2.2)
//node.h
- class Node
- {
- friend class Linklist; //在Node类中声明友元类Linklist
- friend void ShowNode(Node &n);//声明友元函数ShowNode
- public:
- Node();
- Node(Node &n);
- Node(int i,char c='0');
- Node(int i,char c,Node *p,Node *n);
- ~Node();
- static int allocation();
- private:
- int idata;
- char cdata;
- Node *prior;
- Node *next;
- static int count;
- };
//node.cpp
//其余部分同程序16.2.1
- void ShowNode(Node &n)
- {
- cout <<n.idata <<'\t' <<n.cdata <<endl;//友元函数可以访问私有成员数据
- }
//linklist.h同程序16.2.1
//main.cpp
- #include <iostream>
- #include "Linklist.h"
- using namespace std;
- int main()
- {
- int tempi;
- char tempc;
- cout <<"请输入一个整数和一个字符:" <<endl;
- cin >>tempi >>tempc;
- Linklist a(tempi,tempc);
- a.Locate(tempi);
- a.Insert(1,'C');
- a.Insert(2,'B');
- cout <<"After Insert" <<endl;
- a.Show();
- Node b(4,'F');
- cout <<"An independent node created" <<endl;
- cout <<"Use friend function to show node" <<endl;
- ShowNode(b);//用友元函数输出b结点的内容
- return 0;
- }
运行结果:
请输入一个整数和一个字符:
3 F
Node constructor is running...
Linklist constructor is running...
Node constructor is running...
Node constructor is running...
After Insert
3 F
2 B
1 C
Node constructor is running...
An independent node created
Use friend function to show node
4 G
Node destructor is running...
Linklist destructor is running...
Node destructor is running...
Node destructor is running...
Node destructor is running...
我们看到函数ShowNode成功地访问了链表结点b的私有成员数据。所以当一个函数要访问一个或多个对象的私有成员时,我们可以用友元来解决这个问题。
我们使用了友元之后,发现在设计程序的时候方便了很多。原先的那些私有成员都能轻松地被访问了。于是我们不用去写那些繁琐的成员函数,程序执行的时候也减少了函数的调用次数,提高了运行效率。
一个“好朋友”带来的是效率和方便,而一个“坏朋友”却能带来更多的麻烦。友元的存在,破坏了类的封装性。一个类出现问题,就不仅仅是由这个类本身负责了,还可能和它众多的友元有关。这无疑使得检查调试的范围突然扩大了许多,难度也陡然增加。
所以,我们在使用友元的时候,权衡使用友元的利弊,使程序达到最佳的效果。