本文是 友元 专题
什么是友元?英文翻译就是Friends。我们在设计一个类的时候考虑到封装性,访问权限,会把一些成员变量的访问属性定义为private,但是这样定义后,如果后期其它类想要访问这个类中的private访问属性的成员变量时,就需要修改那个类对应成员变量的访问属性,这样就修改了代码。那么如何做到不修改类成员变量访问属性的情况下,可以使其它类或全局函数访问到这个类的private访问属性的成员变量呢?友元,就这么被设计出来了。当然友元还有一种用途:要对流输出符<<重载时,这个后面再讲解。
先说说哪些我们已知的东西可以作为友元?全局函数,成员函数,一个类(中的全部成员函数)。直接看例子吧:
//全局函数作友元
#include <iostream>
#include <cmath>
using namespace std;
class Point{
public:
Point(double x, double y){
_x = x;
_y = y;
}
void getFormatxy();
friend double distance(Point &a, Point &b);
private:
double _x, _y;
};
void Point::getFormatxy(){
cout<<"("<<_x<<","<<_y<<")"<<endl;
}
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()
{
Point p1(3.0, 4.0), p2(6.0, 8.0);
p1.getFormatxy();
p2.getFormatxy();
double d = distance(p1, p2);
cout<<"distance is "<<d<<endl;
return 0;
}
//一个类的成员函数作友元
#include <iostream>
#include <cmath>
using namespace std;
class Point; //前向声明,用于声明。
class ManagerPoint{
public:
double distance(Point &a, Point &b);
};
class Point{
public:
Point(double x, double y){
_x = x;
_y = y;
}
void getFormatxy();
friend double ManagerPoint::distance(Point &a, Point &b);
private:
double _x, _y;
};
void Point::getFormatxy(){
cout<<"("<<_x<<","<<_y<<")"<<endl;
}
double ManagerPoint::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()
{
Point p1(3.0, 4.0), p2(6.0, 8.0);
p1.getFormatxy();
p2.getFormatxy();
ManagerPoint mp;
float d = mp.distance(p1, p2);
cout<<"distance is"<<d<<endl;
return 0;
}
//友元类
#include <iostream>
#include <cmath>
using namespace std;
class Point{
public:
friend class ManagerPoint;
Point(double x, double y){
_x = x;
_y = y;
}
void getFormatxy();
private:
double _x, _y;
};
void Point::getFormatxy(){
cout<<"("<<_x<<","<<_y<<")"<<endl;
}
class ManagerPoint{
public:
double distance(Point &a, Point &b);
};
double ManagerPoint::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()
{
Point p1(3.0, 4.0), p2(6.0, 8.0);
p1.getFormatxy();
p2.getFormatxy();
ManagerPoint mp;
float d = mp.distance(p1, p2);
cout<<"distance is"<< d<<endl;
return 0;
}
友元小结:
在需要允许某些特定的非成员函数访问一个类的私有成员(及受保护成员),而且同时仍阻止一般的访问的情况下,友元是可用的。
优点:可以灵活地实现需要访问若干类的私有或受保护的成员才能完成的任务。便于与其它不支持类概念的语言(如C语言、汇编)进行混合编程。通过使用友元函数重载可以更自然地使用C++语言的IO流库。通过友元直接访问私有成员也是可以提高程序的运行效率的。
缺点:一个类对其非公有成员的访问权限授予其它函数或者类,会破坏类的封装性,降低该类的可靠性和可维护性。
注意:友元关系不能被继承,友元关系不具有传递性。
应将所有的友元关系声明放在类定义体的开始部分,并且在其前面不应带有任何访问权限限定符。
友元关系是授予的,而不是获得的。比如为了使 类B 成为 类A 的友元,类A必须显式地将类B声明为它的友元。
将重载函数指定为类的友元也是可行的,想成为友元的每个重载函数,都必须在类定义中显式地声明成这个类的友元。
声明为谁的友元,就可以通过谁的对象访问谁的私有数据成员
补充:流输出符的重载。
一个问题:为什么C++流输入输出必须重载为友元函数?
因为 cout 类的 operator << 函数的源码无法改变,所以无法重载为类的成员函数,操作符重载过程中,需要用到类的成员变量,所以只是重载全局函数,无法使用类的成员变量,所以将操作符重载为友元函数,如果是重载双目操作符(即为类的成员函数),就只要设置一个参数作为右侧运算量,而左侧运算量就是对象本身。而 >>或<< 左侧运算量是 cin或cout 而不是对象本身,所以不满足后面一点。就只能申明为友元函数了。(其实这个地方本人还没完全理解,欢迎博友留言做进一步的解释!)
用法举例:
#include <iostream>
using namespace std;
class Complex{
friend ostream & operator<<(ostream &os, const Complex &c){
os<<"("<<c._x<<","<<c._y<<")"<<endl;
}
public:
Complex(float x, float y):_x(x),_y(y){}
private:
float _x;
float _y;
};
int main()
{
Complex c(1, 2), c2(2, 3);
cout<<c<<c2; //等价于下面代码
operator<<(operator<<(cout,c), c2);
return 0;
}