友元(友元函数、友元类、类成员函数的友元)

(friend)友元机制:允许一个类将对其非公有成员的访问权限授予指定的函数或者类。

我们可以将友元大致分为3类:

  • 友元函数     
  • 友元类     
  • 类成员函数的友元

 

一:友元函数

友元函数:是指某些虽然不是类的成员函数,但是能够访问这个类的所有成员的函数。

比如我们下列的代码:

#include <iostream>

class Test
{
public:
        Test(int a, int b):ma(a), mb(b){}
	friend void show(Test &rhs);//声明 你show这个函数 是 我Test类的朋友了
private:							//你可以访问我的所有成员
	int ma;
	int mb;
};

void show(Test &rhs)
	{
		rhs.ma++;     //为了验证是否可以访问Test类的私有成员,我们对其进行修改并打印
		std::cout << "ma:" << rhs.ma << std::endl;
		std::cout << "mb:" << rhs.mb << std::endl;
	}

int main()
{
	Test test1(10, 20);
	show(test1);

	return 0;
}

我们可以运行一下结果,看是否和我们预想的一致:

我们通过测试结果可以得出结论:函数show确实访问到了Test类的私有成员。

 

二:友元类

友元类:相当于友元函数的扩张版本,指你友元类的所有成员函数 都是 我这个类的友元函数,你所有的成员函数都可以访问我这个类的所有成员,我对你没有秘密。

应用:当希望一个类可以访问另一个类的私有成员时,可以将该类声明为另一个类的友元类。

 

关于友元类的特点:

  • 友元关系不能被继承     
  • 友元关系是单向的,不具备交换性,例如我声明你是我的朋友,那你可以从我口袋拿钱,但是不代表我就变成了你的朋友,我是不能从你口袋拿钱的。     
  • 友元关系不具有传递性,例如B是A的朋友,C是B的朋友,但是不代表C就变成A的朋友了。

比如我们下列的代码:

#include <iostream>

class Link;

class Node
{
public:
	Node(int val = 0) :mdata(val), pnext(NULL){}
private:
        int mdata;
        Node* pnext;
	friend class Link;//声明类Link是类Node的朋友
	                 //指类Link中的所有成员函数都可以访问类Node中的所有成员
};

class Link           //友元类定义,为了可以访问到类Node中的成员
{
public:
	Link() :phead(new Node()){}

	~Link()
	{
		Node* pCur = phead;
		Node* pNext = pCur;
		while (pCur != NULL)
		{
			pNext = pCur->pnext;
			delete pCur;
			pCur = pNext;
		}
	}

	void insert(int val)
	{
		Node* pnewnode = new Node(val);
		pnewnode->pnext = phead->pnext;
         	phead->pnext = pnewnode;
	}

	void Show()
	{
		Node* pCur = phead->pnext;
		while (pCur != NULL)
		{
			std::cout << pCur->mdata << " ";
			pCur = pCur->pnext;
		}
		std::cout << std::endl;
	}

private:
	Node* phead;
};

int main()
{
	Link link1;
	for (int i = 0; i < 10; i++)
	{
		link1.insert(i + 1);
	}
	link1.Show();

	return 0;
}

我们可以运行一下结果,看是否可以对类Node进行访问:

我们通过测试结果可以得出结论:类Link确实访问到了类Node中的私有成员。

 

三:类成员函数的友元

类成员函数的友元:相当于友元类的缩小版本,指一个类中被声明的特定成员函数 可以 访问到另一个类中的所有成员。

注意:当用到类成员函数的友元时,需要注意友元声明与友元定义之间的相互依赖,比如下面这个例子,类B必须先定义好,才能定义类A的该友元成员函数。

比如我们下列的代码: 
#include <iostream>

class B; //当用到友元成员函数时,需要注意友元声明与友元定义之间的相互依赖。这时类B的声明

class A
{
public:
	void show(B &rhs);//声明该函数是类B的友元函数,这里不能直接写函数体,因为这时还不能访问类B的私有成员
};

class B
{
public:
	B(int a, int b):ma(a), mb(b){}
	friend void A::show(B &rhs);   //这里声明该函数A::show是类B的友元成员函数
private:
	int ma;
	int mb;
};

void A::show(B &rhs)            //这里才是友元成员函数的定义
{                              //只有在定义B之后,才知道该函数是类B的友元,这时才能成功访问类B的所有成员
	                          //所以这时才定义该函数,毕竟,它被设为友元是为了访问类B的成员
	rhs.ma++;
	std::cout << "ma:" << rhs.ma << std::endl;
	std::cout << "mb:" << rhs.mb << std::endl;
}

int main()
{
	A a1;
	B b1(10, 20);

	a1.show(b1);

	return 0;
}

我们可以运行一下结果,看是否函数A::show可以对类B进行访问:

我们通过测试结果可以得出结论:类A中的函数show确实访问到了类B中的私有成员。

 

四:对于友元的建议

  • 我们都知道C++的三大特点:封装、继承、多态,封装通过权限控制保证了类的安全性     
  • 而友元打破了这种封装性,会使其变得不安全     
  • 所以我们建议能不用友元,就尽量不要用友元,不得不用的时候再用友元。