友元分为:友元函数和友元类
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元函数
问题:现在我们尝试去重载operator<<,然后发现我们没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以我们要将operator<<重载成全局函数。但是这样的话,又会导致类外没办法访问成员,那么这里就需要友元来解决。operator>>同理。
解释:
1 2 3 4
| class MyClass { public: MyClass operator+(const MyClass& other); };
|
在这种情况下,调用a + b
实际上被编译器视为a.operator+(b)
。
如果我们将operator<<
重载为类的成员函数,那么左操作数必须是该类的对象,而不是流对象。这显然不符合实际使用的情况,因为我们通常需要的是将对象插入到输出流中。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Date { public: Date(int year, int month, int day) : _year(year) , _month(month) , _day(day) {} ostream& operator<<(const Date& d) { cout << d._year << "-" << d._month << "-" << d._day; return cout; } private: int _year; int _month; int _day; };
int main() { Date d(2017, 12, 24); cout << d << endl; return 0; }
|
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| class Date { public: friend ostream& operator<<(ostream& cout, const Date& d); friend istream& operator>>(istream& cin, Date& d); Date(int year, int month, int day) : _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; }
istream& operator>>(istream& cin, Date& d) { cin >> d._year; cin >> d._month; cin >> d._day; return cin; }
int main() { Date d(2017, 12, 24); cout << d << endl; cin >> d; cout << d << endl; return 0; }
|
代码生成图
1 2 3
| 2017-12-24 2024 06 06 2024-6-6
|
Note:friend ostream &operator<<( const D &d);这个代码会报错
原因
此外,operator<<
的函数参数应该包含两个参数:
- 一个
ostream
类型的对象(通常为引用类型),表示输出流。
- 一个
const D&
类型的对象,表示要输出的对象。
我认为是规定!
友元函数特性
- 友元函数可访问类的私有成员,但不是类的成员函数
- 友元函数不能用const修饰
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
- 一个函数可以是多个类的友元函数
- 友元函数的调用与普通函数的调用和原理相同
友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
- 友元关系是单向的,不具有交换性。
比如下面的Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
- 友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C是A的友元。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| class Date; class Time { friend class Date; public: Time(int hour = 14, int minute= 20, int second = 10) : _hour(hour) , _minute(minute) , _second(second) {} private: int _hour; int _minute; int _second; };
class Date { public: Date (int year = 2000, int month = 01, int day = 16) : _year(year) , _month(month) , _day(day) { }
void SetTimeOfDate(int hour, int minute, int second) { _t._hour = hour; _t._minute = minute; _t._second = second; }
void print() { cout << _year << '-' << _month << '-' << _day << ' '; cout << _t._hour << ':' << _t._minute << ':' << _t._second << endl; } private: int _year; int _month; int _day; Time _t; };
int main() { Date d; d.print();
return 0; }
|
要点总结
- 友元就是让一个外部函数或者外部类能访问我的私有成员。
- 友元打破了原有的权限制度,所以十分危险,不建议使用
转载自:C++:友元函数与友元类的介绍