封裝性要求私有成員不可外部訪問,但某些場景(運算符重載、類間協作、效率關鍵路徑)需要打破封裝。友元提供「受控的封裝破壞」:只對特定函數/類開放私有訪問權。
class Integer {
int val;
friend void print(const Integer&); // 聲明
};
void print(const Integer& obj) { // 定義(不加 friend、不加 類名::)
cout << obj.val;
}
print(obj)。class Girl; // 前置聲明
class Boy {
string name;
friend class Girl; // Girl 所有成員可訪問 Boy 私有成員
};
class Girl {
void show(Boy& b) { cout << b.name; } // 直接訪問
};
friend class Girl 只讓 Girl 訪問 Boy,反之不成立。class Girl;
class Boy {
string name;
friend void Girl::show(Boy&); // 僅 Girl::show 是友元
};
class Girl {
string name;
public:
void show(Boy& b) { cout << b.name; } // 定義在類外或類內均可
};
show 必須已聲明(可在 Girl 類內聲明,類外定義)。Girl g; g.show(boy_obj);operator<< 的友元聲明(高頻陷阱)問題:模板類實例化後,operator<< 也需成為對應實例的友元。若直接聲明非模板友元,會導致所有實例共享同一非模板函數,類型不匹配。
正確寫法:
template <class V, class A>
class MGraph {
// 方案一:函數模板友元(推薦,一對一綁定)
friend ostream& operator<< <>(ostream&, const MGraph<V,A>&);
// 方案二:顯式指定模板參數
friend ostream& operator<< <V,A>(ostream&, const MGraph<V,A>&);
};
定義:
template <class V, class A>
ostream& operator<<(ostream& os, const MGraph<V,A>& G) { ... }
<> 必須保留,表示「這是個函數模板」。<>,編譯器視為非模板函數聲明,鏈接時找不到匹配實例。| 特性 | 成員函數 | 友元函數 |
|---|---|---|
| this 指針 | 有 | 無 |
| 調用語法 | obj.func() | func(obj) |
| 訪問私有成員 | 直接 | 通過參數對象 |
| 運算符重載 | 左操作數必須是本類對象 | 左操作數可為其他類型(如 ostream&) |
operator<<、operator>>、緊密協作的類)。public 區,語義更清晰。friend ... <> 語法。