前言
类中的成员通过权限控制符实现了数据的封装,若对象要访问类中的私有数据,则只能通过成员函数实现。这种方式实现了数据的封装却增加了开销,有时候需要通过外部函数或类直接访问其他类的私有成员,为此C++提供了友元,使用友元可以访问类中的所有成员,函数和类都可以作为友元。
一友元函数
友元函数可以是类外定义的函数或者是其他类中的成员函数,若在类中声明某一函数为友元函数,则该函数可以操作类中的所有数据。
接下来分别讲解类外定义的普通函数作为类的友元函数和类成员函数作为友元函数的用法。
普通函数作为友元函数
将普通函数作为类的友元函数,在类中使用friend关键字声明该普通函数就可以实现,友元函数可以在类中任意位置声明。普通函数作为类的友元函数的声明格式如下所示:
class 类名 { friend 函数返回值类型 友元函数名(形参列表); ... //其他成员 }
如下所示,原本,在friend_show中是不可以访问类myclass的私有成员变量num的。但是将friend_show设置为myclass的友元函数以后,就可以访问了
#include <iostream>#include <iomanip>using namespace std;class myclass{friend void friend_show(myclass &c);public:myclass(int n):num(n){cout << "class init" << endl;}~myclass(){cout << "class destroy" << endl;}void show(){cout << "num = " << setw(5) <<setfill(' ') << num <<endl;}private:int num;};void friend_show(myclass &c){cout << "num = " << setw(5) <<setfill(' ') << c.num <<endl;}int main(){myclass c(10);c.show();friend_show(c);}
运行结果:
class initnum = 10num = 10class destroy
如何将类中的友元注释掉,编译就会报错:如下图所示,提示num是一个私有成员。
其他类的成员函数作为友元函数
代码如下所示:
注意,void show(myclass &c)函数的实现部分,要写在class myclass类体的后面,否则会提示错误。
#include <iostream>#include <iomanip>using namespace std;class myclass;class friendclass{public:friendclass(){}void show();void show(myclass &c);};class myclass{friend void friendclass::show(myclass &c);public:myclass(int n):num(n){cout << "class init" << endl;}~myclass(){cout << "class destroy" << endl;}void show(){cout << "num = " << setw(5) <<setfill(' ') << num <<endl;}private:int num;};void friendclass::show(myclass &c){cout << "num = " << setw(5) <<setfill(' ') << c.num <<endl;}void friendclass::show(){}int main(){myclass c(10);(new friendclass()) ->show(c);}
class initnum = 10class destroy
二 友元类
除了可以声明函数为类的友元函数,还可以将一个类声明为友元类,友元类可以声明在类中任意位置。声明友元类之后,友元类中的所有成员函数都是该类的友元函数,能够访问该类的所有成员。与声明友元函数类似,友元类也是使用关键字friend声明,其语法格式如下:
在前面例子的基础上修改:
#include <iostream>#include <iomanip>using namespace std;class myclass;class friendclass{public:friendclass(){}void show();void show(myclass &c);};class myclass{//修改的这里//friend void friendclass::show(myclass &c);friend class friendclass;public:myclass(int n):num(n){cout << "class init" << endl;}~myclass(){cout << "class destroy" << endl;}void show(){cout << "num = " << setw(5) <<setfill(' ') << num <<endl;}private:int num;};void friendclass::show(myclass &c){cout << "num = " << setw(5) <<setfill(' ') << c.num <<endl;}void friendclass::show(){}int main(){myclass c(10);(new friendclass()) ->show(c);}
class initnum = 10class destroy
三 使用友元应注意的地方
从面向对象程序设计来讲,友元破坏了封装的特性。但由于友元简单易用,因此在实际开发中较为常用,如数据操作、类与类之间消息传递等,可以提高访问效率。使用友元需要注意以下几点:
①友元声明位置由程序设计者决定,且不受类中public、private、protected权限控制符的影响。
②友元关系是单向的,即类A是类B的友元,但B不是A的友元。
③友元关系不具有传递性,即类C是类D的友元,类E是类C的友元,但类E不是类D的友元。④友元关系不能被继承。