友元(friend)机制允许一个类将对其非公有成员的访问权授予指定的函数或类。换句话说就是通过友元来访问类的非公有成员。当然这也是一把双刃剑,在提供便利的同时也在一定程度上破坏了封装性。
友元可以是普通函数,也可以是类,还可以是类的成员函数。
一、友元类
假设有一个Screen类,还有一个Window_Mgr类用于管理一组Screen,那么Window_Mgr类从逻辑上可能需要访问由其管理的Screen类对象的内部数据,包括私有和保护的成员,那么可以将Window_Mgr类声明为Screen类的友元类,具体如下:
class Screen
{
...
friend class Window_Mgr;
...
private:
int height;
int width;
...
};
Window_Mgr的成员可以直接引用Screen的私有成员,比如:Window_Mgr可以有一个函数来重定位一个Screen:
Window_Mgr& Window_Mgr::relocate(int h, int w, Screen& s)
{
s.height += h;
s.width += w;
}
如果缺少友元声明,则这段代码会出错,因为Screen不允许在类的外部访问其私有成员height和width。由于Window_Mgr是Screen类的友元,这一切就都OK了,而且Window_Mgr中的所有函数都可以访问Screen类的任何成员。
二、友元函数
有时候你可能并不需要给予友元类那么大的权限,你仅仅想让某个函数或者某个类的成员函数访问类的私有和保护成员,那么你可以你可以使用友元函数,比如上面的例子,我们可以只允许Window_Mgr类的relocate函数能够访问Screen中的私有和保护成员,那么可以将relocate函数设为Screen类的友元函数:
class Screen
{
...
friend Window_Mgr& Window_Mgr::relocate(int h, int w, Screen&);
...
};
需要注意的是:当将成员函数声明为友元时,函数名必须用该函数所属的类名加上限定。
三、其它事项
1、友元声明与作用域
如果需要声明类的成员函数为友元函数,那么必须先定义类,如上例如果要声明relocate为Screen类的友元函数则必须先定义Window_Mgr类。如果是类或者非成员函数作为友元则无此要求。
2、重载函数与友元关系
类必须将重载函数集中每一个希望设为友元的函数都声明为友元。
3、友元关系与继承
友元关系不能继承。基类的友元对派生类的成员没有特殊访问权限。如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类。
每个类控制对自己的成员的友元关系,具体举例如下:
class Base
{
friend class Frnd;
protected:
int i;
};
class D1 : public Base
{
protected:
int j;
};
class Frnd
{
public:
int mem(Base b) {return b.i;} //ok:Frnd is friend to Base
int mem(D1 d) {return d.i;} //error:friendship does not inherit
};
class D2 : public Frnd
{
public:
int mem(Base b) {return b.i;} //error:friendship does not inherit
};
4、其它关系
友元关系是单向的,不具有交换性。如果类B是类A的友元,类A不一定是类B的友元。
友元关系不具有传递性。如果类B是类A的友元,类C是类B的友元,类C不一定是类A的友元。
阅读(3992) | 评论(1) | 转发(2) |