友元(友元函数、友元类、类成员函数的友元)
(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++的三大特点:封装、继承、多态,封装通过权限控制保证了类的安全性
- 而友元打破了这种封装性,会使其变得不安全
- 所以我们建议能不用友元,就尽量不要用友元,不得不用的时候再用友元。