一、友元函数
友元函数是一种特殊的函数,它需要在类体内进行说明,可以访问类的私有成员和保护成员,但又不是类的成员函数。友元函数的说明如下:
friend 数据类型函数名(参数)
其中,friend是说明友元函数的关键字,友元声明可以出现在类中的任何地方。通常,将友元声明成组地放在类定义的开始或结尾是个好主意。归纳起来,友元函数是一种能够访问类中私有成员的非类成员函数,友元函数在定义上和调用上与普通函数一样。友元的作用在于提高程序的运行效率,但是,它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。因此,只有在十分需要的时候才使用友元函数。 例如:分析下面程序的输出结果
#include
#include
using namespace std;
class TPoint
{
private:
double x,y; //私有数据成员
public:
TPoint(double a,double b) //构造函数
{
x=a,y=b;
cout<
}
friend double distanceoftwopoints(TPoint &a, TPoint &b) //友元函数
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
} ;
int main()
{
TPoint p1(2,2),p2(5,5);
cout<
return 0;
}
上述程序中,distaceoftwopoints()是类TPoint的友元函数,在其中可以直接使用a.x等,由于x等是类TPoint的私有成员,所以在普通函数中是不能这么使用的,但是在友元函数中则可以。下面是该程序的输出。
友元函数的一个重要特点是它可以是多个类的友元。例如,编写一个程序,求一个点到直线的距离。设计一个点类Point,它有两个私有数据成员x和y,表示点的坐标。另一个类为直线类Line,它有三个私有数据成员a,b和c,表示直线方程ax+by+c=0。这两个类中都说明了一个友元函数dist,用于计算一个点到直线的距离。点(x,y)到直线ax+by+c=0的距离d的计算公式如为:d=abs((ax+by+c)/sqrt(a* a+b*b)),其中abs是求绝对值函数,sqrt是求平方根函数。代码如下:
#include
#include
using namespace std;
class Line;//提前说明
class Point
{
int x,y;
public:
Point(int x1,int y1)
{
x=x1; y=y1;
}
friend double dist(Line l, Point p);//友元函数说明
} ;
class Line
{
int a,b,c;
public:
Line(int a1, int b1, int c1)
{
a=a1; b=b1; c=c1;
}
friend double dist(Line l, Point p);//友元函数说明
};
double dist(Line l, Point p)//注意:类内已经说明,因此函数前不要加friend
{
double d;
d = abs(l.a*p.x+l.b*p.y+l.c)/(sqrt(l.a*l.a+l.b*l.b));
return d;
}
int main()
{
Point p(10,10);
Line l(2,4,-3);
cout<
return 0;
}
下面是该程序的输出结果:上述程序中有两个类Point和Line,它们都说明了同一个友元函数:friend double dist(Line l, Point p)。由于在Point类中说明时尚未定义类Line,所以需要在前面用class Line语句进行提前说明。该程序的执行如下:
一个类A的成员 函数f()可以作为另一个类B的友元函数,在这种情况下,必须先定义A类,并且在B类中说明友元函数f()时,要加上成员函数所在类的类名,即:
class A
{
...
数据类型 f(参数表)
...
}
class B
{
...
friend 数据类型 A::f(参数表);
...
}
数据类型 A::f(参数表)
{
函数体;
}二、友元类
除了前面所讲过的友元函数外,友元还可以是一个类,即一个类可以作为另一个类的友元。当一个类作为另外一个类的友元时,就意味着这个类的所有成员函数都是另一个类的友元函数。例如,以下程序说明类B是类A的友元类:
class A
{
...
Public:
friend class B;//说明B是A的友元类
...
}
例如,有如下程序
#include
#include
using namespace std;
class A
{
int x;
public:
A() {x=5;}
friend class B;//说明B是A的友元类
};
class B
{
public:
void disp1(A tmp){
tmp.x++;
cout<
}
void disp2(A tmp){
tmp.x--;
cout<
}
};
int main()
{
A obj1;
B obj2;
obj2.disp1(obj1);
obj2.disp2(obj1);
return 0;
}
}
上述程序中,类B是类A的友元类,类B包括两个成员函数,这两个成员函数可以当成类A自己的友元函数一样使用,该程序执行结果如下:
友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。关于友元类的注意事项:(1)友元关系不能被继承。(2)友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。(3)友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明。
三、习题解析
1.一个类的友元函数能够访问该类的( )。A.私有成员 B.保护成员 C.公有成员 D.所有成员
解析:一个类的友元函数对类成员的访问能力等同于类的成员函数,即能访问类的所有成员,选D。
2.下列的各类函数中,( )不是类的成员函数。A、 构造函数 B、析构函数 C、友元函数 D、拷贝初始化构造函数
答案:C
3.下列关于友元的描述中,错误的是( )A.友元函数可以访问该类的私有数据成员B.一个类的友元类中的成员函数都是这个类的友元函数C.类与类之间的友元关系可以继承D.友元可以提高程序的运行效率
解析:友元关系是不能被继承的,并且是单向的,不可交换的。
4.下列程序利用友元函数求2个点的距离,找出存在的错误(有2个错)。
#include
#include
class Tpoint
{ double x,y;
public:
Tpoint(double a,double b)
{ x=a; y=b; }
friend double distance(Tpoint &, Tpoint &);
};
double Tpoint::distance(Tpoint &p1,Tpoint &p2)
{ double dist;
dist=sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
return dist;
}
int main()
{ Tpoint p1(2,2),p2(5,6);
cout<
return 0;
}四、小结
在需要允许某些特定的非成员函数访问一个类的私有成员(及受保护成员),而同时仍阻止一般的访问的情况下,友元是可用的。
优点:可以灵活地实现需要访问若干类的私有或受保护的成员才能完成的任务;便于与其他不支持类概念的语言(如C语言、汇编等)进行混合编程;通过使用友元函数重载可以更自然地使用C++语言的IO流库,便于实现运算符的重载等功能。
缺点:一个类将对其非公有成员的访问权限授予其他函数或者类,会破坏该类的封装性,降低该类的可靠性和可维护性。
参考书目
《C++ Primer》,《C++语言习题与解析》等