C++ 存取控制基於類 (per-class),而非基於對象 (per-object)。
即:private/protected 成員對同類的所有對象可見,不僅限於 this 對象。
class Trivial {
private:
int val;
public:
Trivial(const Trivial& other) {
val = other.val; // ✅ 合法!other 是同類對象
}
};
若存取控制是 per-object,other.val 應被禁止。實際編譯通過,證明是 per-class。
class Base {
protected:
int val;
};
class Derived : public Base {
public:
void func(Base* b, Derived* d) {
// b->val; // ❌ 編譯錯誤:b 是 Base*,非 Derived 類內
d->val; // ✅ 合法:d 是 Derived*,Derived 類內可訪問 Derived 對象的 protected
val; // ✅ 合法:當前對象 (this) 的 protected
}
};
b->val 錯誤:當前代碼在 Derived 類內,但 b 的靜態類型是 Base*。存取檢查基於訪問代碼所在的類 (Derived) 和被訪問成員聲明的類 (Base)。Derived 只能訪問 Derived 及其基類中對 Derived 可見的成員,但只能通過 Derived 類型的表達式 (包括 this、Derived*、Derived&) 訪問。Base* 表達式不屬於 Derived 類型,故被拒。d->val 合法:d 類型為 Derived*,屬於 Derived 類型表達式。val 合法:隱式 this->val,this 為 Derived*。In C++ access control works on per-class basis, not on per-object basis.
So an instance of class B will always have access to all the members of another instance of class B.
#include <iostream>
#include <string>
class MyClass {
public:
MyClass(const std::string& data) : mData(data) {}
const std::string& getData(const MyClass& instance) const {
return instance.mData; // ✅ 訪問另一對象的 private
}
private:
std::string mData;
};
int main() {
MyClass a("Stack");
MyClass b("Overflow");
std::cout << "b via a = " << a.getData(b) << std::endl; // 輸出 Overflow
return 0;
}
C++17 [class.access] p1:
A member of a class can be accessed ... if the access is from a member or friend of the class ...
關鍵在於 from a member ... of the class —— 判斷依據是訪問代碼所在的類,而非對象身份。
| 場景 | per-object 直覺 | per-class 事實 |
|---|---|---|
| 拷貝/移動構造 | 無法讀取源對象 private | ✅ 正常讀取 |
| operator== 比較 | 無法比較另一對象 private | ✅ 正常比較 |
| 容器內部操作 | 迭代器無法訪問節點 private | ✅ 友元/成員函數正常訪問 |
| 派生類訪問基類 protected | 可通過基類指針訪問 | ❌ 僅限派生類類型表達式 |
friend 或提供公有/保護接口「存取控制是類的屬性,不是對象的屬性」—— 這句話揭示了 C++ 封裝機制的本質:封裝邊界在類之間,不在對象之間。同一類的所有實例互為「自己人」,共享完整訪問權限。這設計既符合直覺 (同類對象協作無障礙),又保證了封裝的語義完整性 (外部類無法隨意侵入)。