friend 關鍵字允許**非成員函數**或**其他類**訪問類的 `private`/`protected` 成員。它打破了封裝邊界,屬於「受控違規」。本文解析友元函數、友元類、友元成員函數三種形式,語法規則、適用場景、濫用危害與現代替代方案。
class Internet {
char name[20], address[20];
public:
Internet(const char* n, const char* a) {
strcpy(name, n); strcpy(address, a);
}
friend void ShowName(const Internet& obj); // 聲明友元
};
// 定義時**不加** Internet::
void ShowName(const Internet& obj) {
cout << obj.name << endl; // 直接訪問 private 成員
}
int main() {
Internet site("中國軟件開發實驗室", "www.cndev-lab.com");
ShowName(site); // 正常調用
}
class Node; // 前向聲明
class List {
Node* head;
friend class Node; // Node 可訪問 List 所有私有成員
};
class Node {
int data; Node* next;
public:
void InsertAfter(List& list, int val) {
// 直接操作 list.head
Node* n = new Node{val, list.head};
list.head = n;
}
};
class B; // 前向聲明
class A {
int secret;
friend void B::AccessA(A&); // 僅授權 B::AccessA
};
class B {
public:
void AccessA(A& a) { cout << a.secret; } // OK
void Other(A& a) { /* cout << a.secret; // 錯誤 */ }
};
| 場景 | 理由 | 替代方案 |
|---|---|---|
運算符重載 (如 operator<<) | 左操作數是流對象,無法作為成員函數 | 無 (標準庫慣例) |
| 工廠/建造者創建私有構造對象 | 構造函數私有,僅工廠可構造 | 靜態工廠方法 + private 構造 |
| 緊密耦合的協作類 (如容器/迭代器) | 迭代器需訪問容器內部節點 | 嵌套類 (自動擁有外部類訪問權) |
| 單元測試訪問私有狀態 | 不想暴露 getter | 測試專用 `#define private public` (僅測試編譯單元) / FRIEND_TEST 宏 |
friend 是 C++ 為極少數合理場景保留的後門。**能不寫就不寫** — 現代 C++ 工具箱裡有更優雅、更安全的方式表達「受控訪問」。