類別封裝隱藏私有成員,僅成員函式可存取。但某些情境下:
友元機制:在類別內以 friend 宣告外部函式/類別,授予其存取私有/保護成員的權限。
class Point {
private:
double x, y;
public:
Point(double xx, double yy) : x(xx), y(yy) {}
void Getxy() { std::cout << "(" << x << "," << y << ")\n"; }
friend double Distance(Point &a, Point &b); // 友元宣告
};
double Distance(Point &a, Point &b) { // 定義如同普通函式
double dx = a.x - b.x; // 直接存取 private x, y
double dy = a.y - b.y;
return std::sqrt(dx*dx + dy*dy);
}
int main() {
Point p1(3,4), p2(6,8);
p1.Getxy(); p2.Getxy();
std::cout << "Distance: " << Distance(p1, p2) << '\n'; // 像普通函式呼叫
return 0;
}
Point:: 前綴,呼叫時無物件前綴。class B; // 前置宣告
class A {
friend class B; // B 所有成員函式皆為 A 的友元
private:
int secret;
};
class B {
public:
void peek(A &a) { std::cout << a.secret; } // 合法存取 A::secret
};
| 原則 | 說明 | 程式碼驗證 |
|---|---|---|
| 不可繼承 | 衍生類別不自動獲得基類友元權限 | class C:public B { void test(A& a){ a.secret; } } // ❌ 編譯錯誤 |
| 無交換性 | A 友元 B ≠ B 友元 A | 需在 B 中再宣告 friend class A |
| 無傳遞性 | A 友元 B、B 友元 C ⇒ C 不一定友元 A | 需在 A 中額外宣告 friend class C |
// A 為 B 基類,C 為 D 基類
// A 宣告 C 為友元
class A { friend class C; private: int a; };
class B : public A { void f() { a = 1; } }; // B 繼承 A 的方法可存取 C 的私有成員
class C { private: int c; friend class A; };
class D : public C { void g() { c = 2; } }; // D 新增方法無法存取 A 的私有成員
| 優點 | 缺點 |
|---|---|
| 消除 getter/setter 呼叫開銷 | 破壞封裝,增加耦合 |
| 運算子多載(如 <<, +)語法自然 | 維護困難:私有結構變更波及所有友元 |
| 單元測試白箱檢查內部狀態 | 濫用導致「人人都是友元」,退化為結構體 |
friend void foo(); 限定單一函式,比整個類別友元更精確。友元是 C++ 的「特權通行證」——用得好是利器,濫用即成毒藥。理解其語義邊界與繼承互動,才能在效能與封裝間取得平衡。