友元
一般来说,类的公有成员能够在类外访问,私有的成员只能被类的其他成员函数访问。
在C++中,可以定义友元,如果某一个函数定义为类的友元,则该函数就可以访问该类的私有函数。也可以把一个类定义为另一个类的友元。
友元函数
如果在某个类的定义中用friend
声明了一个外部函数(或者是其他类的成员函数,既可以是public
型,也可以是private
型)后,这个外部函数称为类的友元函数。
C++提供一种允许外部类和函数存取类的私有成员和保护成员的辅助方法,即将它们声明为一个给定类的友元(或友元函数),使其具有类成员函数的访问权限,但友元本身不是类的成员,它不属于任何类。
使用:
- 关键字:
friend
开头的函数原型,声明可以放在类的私有部分,或者公有部分,无区别,都说明是该类的一个友元函数,但它不是该类的成员函数,不属于任何类; - 友元函数的定义,可以在类的内部或外部;
- 在类外定义友元函数时,与普通函数的定义一样,不应在函数名前用类名加以限制。因此友元函数不像成员函数那样在调用时使用对象名,友元函数要多类的成员进行访问,必须在参数表中显式指明要访问的对象。
- 友元函数是能访问类的所有成员的普通函数,一个函数可以是多个类的友元函数,只需要在各个类中分别声明;
- 调用与一般函数的调用方式及原理一致;
- C++不允许将构造函数、析构函数和虚函数声明为友元函数
友元的非成员函数
友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,需要在类的定义中加以声明,声明时只需在友元的名称前加关键字**friend
**,
friend 函数原型;
非成员函数的友元函数有如下特点。
- 在类内只需对函数进行声明,声明位置没有要求,可以出现在类的任何地方,包括在
private
和public
部分。 - 函数定义要放在类外,具体定义位置没有要求。
- 友元函数不能直接访问类的成员,必须通过访问对象来实现。为此,必须传递类对象作为函数参数,采用传值、传指针或传引用方式均可,出于对程序执行效率的考虑,建议使用类对象的引用作为参数。
- 友元函数不是类的成员函数,所以友元函数的实现和普通函数一样,在实现时不用作用域限定符“::”指示属于哪个类,只有成员函数才使用“::”作用域符号。
- 一个函数可以同时作为多个类的友元函数。
非成员形式的友元函数代码如下所示。
//友元函数的例子
#include <iostream>
#include <math.h>
using namespace std;
class Point
{
public:Point(double i, double j) //构造函数,带默认参数值{x = i;y = j;}void disp(){cout<<"("<<x<<","<<y<<")"<<endl;}private:double x;double y;friend double dis(Point &a, Point &b); //友元函数的声明
};double dis(Point &a, Point &b) //类外,友元函数的定义
{double dx=a.x-b.x; //友元函数可以访问类的private成员double dy=a.y-b.y;return sqrt(dx*dx+dy*dy);
}int main()
{Point p1(3.0,4.0); //声明两个Point类的对象p1和p2;Point p2(6.0,8.0);p1.disp(); //显示p1和p2点的信息p2.disp(); cout<<"Distance is "<<dis(p1,p2)<<endl; //友元函数的调用return 0;
}
友元的成员函数
除了一般的函数可以作为类的友元外,一个类的成员函数也可以作为另一个类的友元。这样的函数不仅可以访问本类的所有成员,还可以访问其友元类的所有成员。需要注意的是,当在一个类中的某个成员函数定义为另一个类的友元函数时需要首先定义此类。
例如,要定义类A的某个函数为类B的友元函数,那么需要先定义类B,然后再进行类A的成员函数与B友元的定义。
对于一个友元成员来说,它不仅可以访问自己所在类中的私有成员和公有成员,同时还可以的一个对声明为友元类的类的成员进行访问。使用友元成员可以使得两个类之间出现互相访问“门”。这样解决了由于类的保护机制而出现的其他类绝对不允许访问的情况。
成员形式的友元函数代码如下所示。
//友元函数的例子
#include <iostream>
using namespace std;
class Point; //声明Point类:在line类的dis函数中传递对象参数。因此必须在line类之前对Point类 //声明,且由于PoInt类的定义在后面,只能对Point对象采取传引用或传指针处理;
class line //友元函数所在的类必须选定义,即line类必须定义在Point类前面
{public:double dis(Point &p1, Point &p2); //友元函数的原型,作为line类的成员函数
};class Point
{
public:Point(double i, double j) //构造函数,带默认参数值{x = i;y = j;}void disp(){cout<<"("<<x<<","<<y<<")"<<endl;}private:double x;double y;friend double line::dis(Point &a, Point &b); //友元的声明
};double line::dis(Point &a, Point &b) //line类内的成员函数dis()的实现,作为Point类的友元函数;
{double dx=a.x-b.x; //友元函数可以访问类的private成员double dy=a.y-b.y;return sqrt(dx*dx+dy*dy);
}int main()
{line line1; //声明一个line类的对象line1;Point p1(3.0,4.0); //声明两个Point类的对象p1和p2;Point p2(6.0,8.0);p1.disp(); //显示p1和p2点的信息p2.disp(); cout<<"Distance is "<<line1.dis(p1,p2)<<endl; //友元函数的调用return 0;
}
友元类
友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。
定义友元类的语句格式如下:
friend class 类名(即友元类的类名);
其中:friend
和class
是关键字。类名必须是程序中的一个已定义过的类。
//类B是类A的友元类
class A
{
Public:friend class B;
};
友元类B中的所有成员函数都是类A的友元函数,能存取类A的私有成员和保护成员。
当把B声明为A类的友元类时,并不一定要求先定义B,只要先对其进行声明即可,这点区别于友元的成员函数。
- 友元关系是单向的,不具有交换性。若类X是类Y的友元,类Y不一定是类X的友元,要看在类中是否有相应的声明。
- 友元关系不具有传递性。若类X是类Y的友元,类Y是Z的友元,类X不一定是类Z的友元,同样要看类中是否有相应的申明。
- 友元关系不能被继承。