14 友元
作用:在程序里,有些私有属性也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术。友元的目的就是让一个函数或者类 访问另一个类中的私有成员,友元的关键字为 ==friend==。
友元的三种实现:
- 全局函数做友元
- 类做友元
- 成员函数做友元
提示:友元关系是单向的、不能传递的:A 是 B 的友元,B 不一定是 A 的友元;A 是 B 的友元、B 是 C 的友元,A 也不一定是 C 的友元。
14.1 全局函数做友元
作用:让一个全局函数能够访问另一个类中的私有成员。
语法:在类的内部用 friend 关键字声明该全局函数,写法为 friend void goodGay(Building* building);。friend 声明与访问权限无关,写在 public、private、protected 区域效果完全一样,通常写在类的开头。
示例:楼房类 Building 中有客厅 m_SittingRoom 和卧室 m_BedRoom 两个私有成员,全局函数 goodGay(好基友)想去参观,就必须在 Building 类的内部用 friend 声明它,否则没有访问权限。
1 |
|
注意:如果不写
friend void goodGay(Building* building);这一行,goodGay 中访问 m_SittingRoom、m_BedRoom 的两行语句都会报错——类的私有成员在类外不能直接访问,加上 friend 声明后才获得访问权限。本例的运行结果为:好基友全局函数正在访问:客厅;好基友全局函数正在访问:卧室。
提示:类的公有成员本来就可以在类外直接访问,友元解决的是私有成员的访问问题;本例特意把两个成员都设为 ==private==,更能看出 friend 声明的作用。
14.2 类做友元
作用:让一个类中的所有成员函数都可以访问另一个类中的私有成员。
语法:在 Building 类的内部声明友元类,写法为 friend class GoodGay;。这条声明授权的是整个类,GoodGay 中的每个成员函数都能访问 Building 的私有成员。
示例:好基友类 GoodGay 中有一个 visit 成员函数去参观楼房 Building。Building 把 GoodGay 整个类声明为友元类,于是 visit 就能访问 Building 的私有成员 m_BedRoom。
1 |
|
注意:GoodGay 中 new 出来的堆区对象在本例结束前没有释放,实际项目中应在析构函数中 delete,这里为聚焦友元语法从简。本例的运行结果为:visit函数正在访问:客厅;visit函数正在访问:卧室。
提示:友元关系是单向的:Building 允许 GoodGay 访问自己的私有成员,反过来 Building 依然不能访问 GoodGay 的私有成员;只想开放某一个成员函数时,用下一节的成员函数做友元。
14.3 成员函数做友元
作用:只让另一个类中的某个成员函数访问本类的私有成员,把开放范围控制到最小。
语法:在 Building 类的内部声明友元成员函数,写法为 friend void GoodGay::visit();,声明时必须带上该成员函数所属的类名和作用域符 ::。
示例:只让 GoodGay 类中的 visit 成为 Building 的友元,visit 可以访问 Building 的私有成员 m_BedRoom;visit2 没有被声明为友元,不可以访问。
注意:成员函数做友元时,声明顺序很讲究,必须按三步走:第一步,先用
class Building;对 Building 做前向声明,因为 GoodGay 类中要存放Building*指针;第二步,声明 GoodGay 类,让编译器知道有 visit 这个成员函数;第三步,再声明 Building 类并在类内写friend void GoodGay::visit();,两个类的成员函数都放在类外实现。顺序写错,编译器会报找不到GoodGay::visit的错误。
1 |
|
注意:
friend void GoodGay::visit();中的::不能丢——它是在告诉编译器”是 GoodGay 类里的那个 visit”,这与全局函数做友元friend void goodGay(Building* building);的写法不同。本例的运行结果为:visit函数正在访问:客厅;visit函数正在访问:卧室;visit2函数正在访问:客厅。
总结:三种友元的本质都是在被访问的类内部用 friend 做一条声明:全局函数做友元直接写函数原型,类做友元写
friend class 类名;全类开放,成员函数做友元写friend 类名::函数名(参数);只开放一个函数。友元使用方便,但也破坏了类的封装性,实际开发中应谨慎使用。