友元
友元提供了一種突破封裝的方式,有時提供了便利。但是友元會增加耦合度,破壞了封裝,所以 友元不宜多用。友元分為:友元函數和友元類
友元函數
問題
現在嘗試去在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的友元。
- 友元關系不能繼承。