私有成员数据除了可能被别的类访问之外,也可能被别的函数或别的类的部分成员函数访问。为了保证类的封装性,我们可以以函数作为单位,“对外开放”类的私有成员。与声明友元类类似,如果我们想用函数访问链表结点类的私有成员数据,则那些函数必须得到链表结点类的认可。声明友元函数的语句格式为:
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的私有成员数据。所以当一个函数要访问一个或多个对象的私有成员时,我们可以用友元来解决这个问题。
我们使用了友元之后,发现在设计程序的时候方便了很多。原先的那些私有成员都能轻松地被访问了。于是我们不用去写那些繁琐的成员函数,程序执行的时候也减少了函数的调用次数,提高了运行效率。
一个“好朋友”带来的是效率和方便,而一个“坏朋友”却能带来更多的麻烦。友元的存在,破坏了类的封装性。一个类出现问题,就不仅仅是由这个类本身负责了,还可能和它众多的友元有关。这无疑使得检查调试的范围突然扩大了许多,难度也陡然增加。
所以,我们在使用友元的时候,权衡使用友元的利弊,使程序达到最佳的效果。