类的友元
我们知道封装是面向对象程序设计的重要特征之一,通过将数据与处理数据的函数封装在类中,可以实现数据的隐藏。但有些时候封装也带来一些不便,需要在设计时做一些妥协。考虑如下的场景:
之前定义的点类Point描述了平面直角坐标系里的点,如果现在需要定义一个Distance函数计算两个点的距离,应该如何实现?
如果把Distance函数作为Point类的成员函数似乎不合适,因为对于一个点来说,计算其距离是没有意义的,也就是说Point类不需要计算距离的功能;如果把Distance函数作为Point类的外部函数,则在计算距离时需要知道两个Point类的坐标X,Y,而坐标X,Y是Point类的私有成员,在类的外部无法直接访问。
为了解决上述问题,C++中引入了友元机制,可以实现在类的外部访问类的私有成员。 对于上述的全局函数Distance,我们在声明Point类的时候使用friend 关键字将其声明为Point类的友元函数,具体方法如下:
friend float Distance(Point &a,Point &b);
这表示Distance函数与Point类之间具备友元关系,在Distance函数里面就可以通过直接访问Point类的私有成员X,Y来计算两个点的坐标了。
友元代表着不同类或对象的成员函数之间或者成员函数与一般函数之间进行数据共享的机制。友元关系就是在一个类中主动声明一个函数或者类是自己的朋友,并为其提供访问本类封装数据的特权。
通过友元关系,一个普通函数或者类的成员函数可以直接访问另一个类中封装的数据。如果在类中声明的友元是一般函数或者类的成员函数,则称之为友元函数;如果声明的友元是一个类,则称之为友元类,该类的所有成员函数都自动称为友元函数。
由此可见,友元机制是对封装机制的破坏,为了确保数据的完整性,避免破坏数据封装与隐藏的原则,建议尽量不使用或者少使用友元。
友元函数
友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,也可以是其他类的成员函数 ,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend 即可.
其格式如下:
friend 类型 函数名(形式参数);
友元函数的声明可以放在类的私有部分,也可以放在公有部分,它们是没有区别的,都说明是该类的一个友元函数,在其函数体内可以通过对象名访问类的私有成员。
一个函数可以是多个类的友元函数,但需要在各个类中分别声明。友元函数的调用和一般函数的调用方法和原理一致!
如下例所示:
#include <iostream>
#include <cmath>
using namespace std;
class Point{
private:
int X,Y;
public:
Point(int xx = 0,int yy = 0){
X = xx;
Y = yy;
}
int GetX(){
return X;
}
int GetY(){
return Y;
}
friend double Distance(Point &a,Point &b);
};
double Distance(Point &a,Point &b){
double dx = a.X - b.X;
double dy = a.Y - b.Y;
return sqrt(dx*dx+dy*dy);
}
int main(void)
{
Point p1(3,5),p2(4,6);
double d = Distance(p1,p2);
cout<<"The distance is "<<d<<endl;
return 0;
}
运行结果如下:
上例中计算两个点的距离函数Distance是一个外部函数,在计算两点的距离时需要访问点的坐标X,Y。而点的坐标数据是Point类的私有成员,不允许在Point类的外部直接访问。因此需要将Distance函数声明为类Point的友元函数,相当于授权使其可以访问自己的私有成员。
友元函数除了可以是普通的外部函数之外,也可以是一个类的成员函数。友元成员函数的声明与使用方法与一般友元函数完全相同。
友元类
一个类可以声明自己的友元函数,还可以声明自己的友元类。
友元类的所有成员函数都是另一个类的友元函数,都可以直接访问另一个类中的私有成员和保护成员。当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。声明友元类的语法格式如下:
friend class 类名;
其中friend 和 class 是关键字,类名必须是程序中一个已定义过的类
class A{
friend class B;
public:
void Display(){
cout<<x<<endl;
}
private:
int x;
};
class B{
public:
void Set(int i);
void Display();
private:
A a;
};
void B::set(int i){
a.X = i;
}
void B::Display(){
a.Display();
}
在上述代码片段中,类A将类B声明为其友元类,则类B的两个成员函数Set(int i)与Display()自动成为类A的友元函数,它们都可以直接访问类A的私有成员和保护成员。
友元关系有以下几点注意事项:
(1) 友元关系是单向的。
如果声明B类是A类的友元,B类的成员函数就可以直接访问A类的私有数据和保护数据,但A类的成员函数却不能直接访问B类的私有数据和保护数据。
(2) 友元关系不可以传递。
(3) 友元关系不可以继承。