友元
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以 友元不宜多用。友元分为:友元函数和友元类
友元函数
问题
现在尝试去在Date类里重载operator<<。无论怎样设置参数,只要他是非静态成员函数,它的第一个参数必定是隐含的this指针,又因为操作数的顺序就是传入参数的顺序,所以,我们要使用这个运算符重载就必须:date<<cout,这不符合使用习惯。
class Date { public: Date(int year, int month, int day): _year(year), _month(month), _day(day){} ostream& operator<<(ostream& _cout){_cout << _year << "-" << _month << "-" << _day << endl;return _cout;} private: int _year; int _month; int _day; };int main() {Date date(2002,7,11);date<<cout;// 因为成员函数第一个参数一定是隐藏的this,所以date必须放在<<的左侧 //虽然结果正确,但不符合正常使用的顺序 }
使用
显而易见可以想到的是,我们把运算符重载作为类外函数,而不是成员函数,这样我们就可以随心调整参数的位置。然而,这又引发了问题,类外函数没有权限访问类内成员(一般类成员变量都是私有),也就没法打印日期信息了。
友元函数解决了类外函数访问类内成员的权限问题!:
class Date {friend ostream& operator<<(ostream& _cout, const Date& d);//声明友元函数 public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){} private:int _year;int _month;int _day; }; ostream& operator<<(ostream& _cout, const Date& d) {_cout << d._year << "-" << d._month << "-" << d._day;return _cout; }int main() {Date date;cout << date << endl;return 0; }
在一个类内使用friend声明一个函数,这个函数就叫做这个类的友元函数,可以访问类内的所有成员,就像成员函数一样(实际上不是)。
特性
- 友元函数可访问类的私有和保护成员,但不是类的成员函数
- 友元函数不能用const修饰(指的是用const修饰函数,也就是修饰this指针)
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
- 一个函数可以是多个类的友元函数
- 友元函数的调用与普通函数的调用原理相同
友元类
使用
class Time {friend class Date;// 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量 public:Time(int hour = 0, int minute = 0, int second = 0): _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 SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_t._hour = hour;_t._minute = minute;_t._second = second;}private:int _year;int _month;int _day;Time _t; };
特性
- 友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
友元特性
- 友元关系是单向的,不具有交换性。 比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接 访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
- 友元关系不能传递。如果C是B的友元, B是A的友元,则不能说明C时A的友元。
- 友元关系不能继承。