我们都知道,C++是一种面向对象的语言,在之前的内容中我们说过,为了区别成员变量不同的访问权限,C++采用了三种不同的权限限定符(分别是:public,private,protect)以满足不同用户的访问需求,同时对一些重要的成员进行保护。但是在这看似美妙的外表下同时也蕴含着一些小问题,就比如今天我们要引出的一个概念——友元。
在一般情况下,我们允许类中的一些特定的成员函数访问其中的私有成员,同时拒绝一般的访问请求,但是我们在实际使用中需要有一些操作经常访问这些私有元素,那么就需要在保护私有成员的同时给予某些特殊的操作一些小小的特权。
友元
友元包括了 友元函数 和 友元类 两个方面,我们一一介绍对比
友元函数
概念
友远函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类中声明,声明时需要加 friend 关键字。
特性
- 一个函数可以是多个类的友元函数,可以对不同类进行访问,满足其特殊功能的要求。
- 友元函数不能用const关键字进行修饰
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
4.友元函数可访问类的私有和保护成员,但不属于类的成员函数,仅仅在类中声明有这个东西,在其他地方进行定义使用。- 友元函数的调用与普通函数的调用和原理相同
6.添加friend表示修饰的函数可以直接调用类中的私有成员,具有单向性,不可跨越性
例子
比如我们需要在任何地方都可以通过 cout<< 输出一个类中的成员函数,使用friend关键字在类中声明<<的重载函数。
有两种书写形式:
简写形式:cout<<d;
完整形式:operator(cout,d);
class Date
{
friend void& operator<<(ostream& _cout, const Date& d);//运算符<<重载声明,注意这里是一般性的声明,姑且使其返回值为void
public:
Date(int year, int month,int day)
{
_y=year;
_m=month;
_d=day;
}
private:
int _y;
int _m;
int _d;
};
void& operator<<(ostream& _cout, const Date& d)
{
_cout<<"the year : "<< d._y <<endl;
_cout<<"the month : "<< d._m << endl;
_cout<<"the day : " << d._d << endl;
}
int main()
{
Date d(2020,3,15);
cout<<d;
return 0;
}
【注意】
以上并不能支持连续的输出,比如 cout<<d<<endl;或者cout <<d<<d;
【分析】
从输出函数的具体调用过程上寻找发生错误的原因,上述连续输出如果按照完整形式写出的话,如下:operator<<(operator<<(cout,d),endl); ,先执行 (operator<<(cout,d) ,该语句的返回值是一个void,但是同时它自动作为了外部operator的第一个参数(本来应为ostream& _cout类型),函数类型不匹配,因此不能调用。
【解决方案】
将其返回值重新置为-cout,使其可以作为下一次到用的第一个参数。这里将正确的<<与>>同时写出
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int year, int month,int day)
{ _y=year;
_m=month;
_d=day;
}
private:
int _y;
int _m;
int _d;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout<<"the year : "<< d._y <<endl;
_cout<<"the month : "<< d._m << endl;
_cout<<"the day : " << d._d << endl;
return _cout;
}
友元类
其声明与友元函数规则相似,在这种情况下,友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
友元类的特性
友元关系是单向的,不具有交换性。即友元并不是相互的,不能进行相互之间的访问,只能单向访问。
友元关系不能传递,比如我们都学过平行线之间具有传递性,即a // b,b // c,a // c,但是友元关系没有这种性质
例
class Date;//前置声明
class Time
{
friend class Date; //声明Date类是Time的友元类,在Date类中,可以直接访问Time中的私有成员变量
public:
Time(int hour=0,int minute=0,int second=0)
{
if(hour>24 || hour<0 || minute>59 || minute <0 || second >59 || second<0)
{
_hour=0;
_minute=0;
_second=0;
}
else{
_hour=hour;
_minute=minute;
_second=second;
}
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
void SetT(int hour, int minute, int second)
{//可以直接访问私有成员变量
if(hour>24 || hour<0 || minute>59 || minute <0 || second >59 || second<0)
{
T._hour=0;
T._minute=0;
T._second=0;
}
else{
T. _hour=hour;
T._minute=minute;
T._second=second;}
}
private:
Time T;
int _year;
int _month;
int _day;
};