在编写链表类的时候我们有着这样的困惑:链表类和链表结点类都是我们编写的,我们能保证链表类对链表结点类的操作都是安全的。但由于类的封装性,我们不得不编写一些成员函数,以便于链表类访问链表结点类的私有成员数据。好在链表结点类的成员数据并不是很多,否则岂不是需要一大堆成员函数来供别的类访问?对于这种情况,我们能否告诉链表结点类:“链表类是安全的,让它访问你的私有成员吧”?
在C++中,可以用友元来解决这种尴尬的问题。所谓友元,就是作为一个类的“朋友”,可以例外地访问它的私有成员数据或私有成员函数。
类似于链表类和链表结点类的问题,我们可以用友元类来解决。即链表类是链表结点类的“朋友”,可以直接访问链表结点类的私有成员数据或私有成员函数。显然,要做链表结点类的“朋友”,必须要得到链表结点类的认可。所以我们必须在链表结点类的声明中告诉电脑,链表类是它认可的“朋友”,可以访问它的私有成员。声明友元类的语句格式为:
friend class 类名;
下面我们来看一下,友元是如何让我们更方便地设计程序的:(程序16.2.1)
//node.h
class Node//声明一个链表结点类
{
friend class Linklist;//在Node类中声明友元类Linklist
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
#include "node.h"
#include <iostream>
using namespace std;
int Node::count=0;
Node::Node()
{
cout <<"Node constructor is running..." <<endl;
count++;
idata=0;
cdata='0';
prior=NULL;
next=NULL;
}
Node::Node(int i,char c)
{
cout <<"Node constructor is running..." <<endl;
count++;
idata=i;
cdata=c;
prior=NULL;
next=NULL;
}
Node::Node(int i,char c,Node *p,Node *n)
{
cout <<"Node constructor is running..." <<endl;
count++;
idata=i;
cdata=c;
prior=p;
next=n;
}
Node::Node(Node &n)
{
count++;
idata=n.idata;
cdata=n.cdata;
prior=n.prior;
next=n.next;
}
Node::~Node()
{
count--;
cout <<"Node destructor is running..." <<endl;
}
int Node::allocation()
{
return count;
}
//linklist.h
#include "node.h"
#include <iostream>
using namespace std;
class Linklist//定义一个链表类
{
public:
Linklist(int i,char c);
Linklist(Linklist &l);
~Linklist();
bool Locate(int i);
bool Locate(char c);
bool Insert(int i=0,char c='0');
bool Delete();
void Show();
void Destroy();
private:
Node head;
Node * pcurrent;
};
Linklist::Linklist(int i,char c):head(i,c)//链表的构造函数
{
cout<<"Linklist constructor is running..."<<endl;
pcurrent=&head;
}
Linklist::Linklist(Linklist &l):head(l.head)
{
cout<<"Linklist Deep cloner running..." <<endl;
pcurrent=&head;
Node * ptemp1=l.head.next;//直接访问私有成员数据
while(ptemp1!=NULL)
{
Node * ptemp2=new Node(ptemp1->idata,ptemp1->cdata,pcurrent,NULL);
pcurrent->next=ptemp2;
pcurrent=pcurrent->next;
ptemp1=ptemp1->next;
}
}
Linklist::~Linklist()
{
cout<<"Linklist destructor is running..."<<endl;
Destroy();
}
bool Linklist::Locate(int i)
{
Node * ptemp=&head;
while(ptemp!=NULL)
{
if(ptemp->idata==i)
{
pcurrent=ptemp;
return true;
}
ptemp=ptemp->next;
}
return false;
}
bool Linklist::Locate(char c)
{
Node * ptemp=&head;
while(ptemp!=NULL)
{
if(ptemp->cdata==c)
{
pcurrent=ptemp;
return true;
}
ptemp=ptemp->next;
}
return false;
}
bool Linklist::Insert(int i,char c)
{
if(pcurrent!=NULL)
{
Node * temp=new Node(i,c,pcurrent,pcurrent->next);
if (pcurrent->next!=NULL)
{
pcurrent->next->prior=temp;
}
pcurrent->next=temp;
return true;
}
else
{
return false;
}
}
bool Linklist::Delete()
{
if(pcurrent!=NULL && pcurrent!=&head)
{
Node * temp=pcurrent;
if (temp->next!=NULL)
{
temp->next->prior=pcurrent->prior;
}
temp->prior->next=pcurrent->next;
pcurrent=temp->prior;
delete temp;
return true;
}
else
{
return false;
}
}
void Linklist::Show()
{
Node * ptemp=&head;
while (ptemp!=NULL)
{
cout <<ptemp->idata <<'\t' <<ptemp->cdata <<endl;
ptemp=ptemp->next;
}
}
void Linklist::Destroy()
{
Node * ptemp1=head.next;
while (ptemp1!=NULL)
{
Node * ptemp2=ptemp1->next;
delete ptemp1;
ptemp1=ptemp2;
}
head.next=NULL;
}
//main.cpp 同程序16.1
运行结果:
请输入一个整数和一个字符:
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 Allocation:3
Node constructor is running...
An independent node created
Node Allocation:4
Node destructor is running...
Linklist destructor is running...
Node destructor is running...
Node destructor is running...
Node destructor is running...
可以看到,程序的运行结果和程序16.1的结果一样,但是链表结点类没有程序16.1中那么繁琐。并且在链表类中完全都是直接访问链表结点类的成员数据,大大减少了调用函数产生的开销,这样执行程序的效率也就得以提高了。