零、声明
本文转载自链接:本文链接:https://blog.csdn.net/Jacky_Feng/article/details/109533248,对于其中部分内容有所修改,侵删。
一、友元函数
结合着类的特性,可知:类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的。非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。
为了解决上述问题,提出一种使用友元的方案。友元是一种定义在类外部的普通函数,但它需要在类体内进行声明,为了与该类的成员函数加以区别,在声明时前面加以关键字friend。友元不是成员函数,但是它可以访问类中的私有成员。友元的作用在于提高程序的运行效率,但是,它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。
1.1 案例分析
问题:现在我们尝试去重载operator<<
,然后发现我们没办法将operator<<
重载成成员函数。因为cout
的输出流对象和隐含的this
指针在抢占第一个参数的位置。this
指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以我们要将operator<<
重载成全局函数。但是这样的话,又会导致类外没办法访问成员,那么这里就需要友元来解决。operator>>
同理。
#include <iostream>
#include <stdio.h>
using namespace std;
class Date {
friend ostream &operator<<(ostream &_cout, Date &d);
friend istream &operator>>(istream &_cin, Date &d);
private:
int _year;
int _month;
int _day;
};
ostream &operator<<(ostream &_cout, Date &d) {
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
istream &operator>>(istream &_cin, Date &d) {
string s;
_cin >> s;
sscanf(s.c_str(), "%d-%d-%d", &d._year, &d._month, &d._day);
return _cin;
}
int main(int argc, char *argv[]) {
Date d;
cin >> d;
cout << d << endl;
return 0;
}
1.2 说明
- 友元函数可访问类的私有成员,但不是类的成员函数
- 友元函数不能用const修饰
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
- 一个函数可以是多个类的友元函数
- 友元函数的调用与普通函数的调用和原理相同
二、友元类
友元除了友元函数以外,友元还可以是类——友元类,即一个类可以作另一个类的友元。当一个类作为另一个类的友元时,这就意味着这个类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的非公有成员。
特性:
-
友元关系是单向的,不具有交换性。
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。 -
友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元。 -
友元关系不能被继承,但对已有的方法来说访问权限不改变。
class Date;
class Time {
friend class Date;
private:
int _hour;
int _minute;
int _second;
};
class Date {
public:
void SetTimeofDate(int hour, int minute, int second) {
_time._hour = hour;
_time._minute = minute;
_time._second = second;
}
private:
int _year;
int _month;
int _day;
Time _time;
};
三、优缺点
利用friend
修饰符,可以让一些普通函数 或 另一个类的成员函数 直接对某个类的保护成员和私有成员进行操作,提高了程序的运行效率;同时避免把类的成员都声明为public
,最大限度地保护数据成员的安全。
但是,即使是最大限度地保护数据成员,友元也破坏了类的封装性。