訪問說明符
在 類/結構體 或 聯合體 的 成員-規範 中,定義後續成員的可訪問性。
在 派生類 宣告的 基類-規範 中,定義後續基類的繼承成員的可訪問性。
目錄 |
[編輯] 語法
public : 成員-宣告 |
(1) | ||||||||
protected : 成員-宣告 |
(2) | ||||||||
private : 成員-宣告 |
(3) | ||||||||
public 基類 | (4) | ||||||||
protected 基類 | (5) | ||||||||
private 基類 | (6) | ||||||||
無論公有、保護還是私有繼承,基類的私有成員對於派生類始終不可訪問。
[編輯] 解釋
每個 類 成員的名稱(靜態、非靜態、函式、型別等)都有一個關聯的“成員訪問許可權”。當程式中任何地方使用成員名稱時,都會檢查其訪問許可權,如果它不滿足訪問規則,程式將無法編譯。
#include <iostream> class Example { public: // all declarations after this point are public void add(int x) // member "add" has public access { n += x; // OK: private Example::n can be accessed from Example::add } private: // all declarations after this point are private int n = 0; // member "n" has private access }; int main() { Example e; e.add(1); // OK: public Example::add can be accessed from main // e.n = 7; // error: private Example::n cannot be accessed from main }
訪問說明符使類的作者能夠決定哪些類成員可供類的使用者訪問(即,介面),哪些成員用於類的內部使用(實現)。
[編輯] 詳細說明
類的所有成員(成員函式 的函式體、成員物件的初始化器以及完整的 巢狀類定義)都可以訪問該類可以訪問的所有名稱。成員函式中的區域性類可以訪問該成員函式可以訪問的所有名稱。
用關鍵字 class
定義的類預設情況下其成員和基類具有私有訪問許可權。用關鍵字 struct
定義的類預設情況下其成員和基類具有公有訪問許可權。聯合體 預設情況下其成員具有公有訪問許可權。
為了授予附加函式或類對保護或私有成員的訪問許可權,可以使用 友元宣告。
可訪問性適用於所有名稱,無論其來源如何,因此會檢查由 typedef 或 using 宣告(繼承建構函式除外)引入的名稱,而不是其引用的名稱。
class A : X { class B {}; // B is private in A public: typedef B BB; // BB is public }; void f() { A::B y; // error: A::B is private A::BB x; // OK: A::BB is public }
成員訪問不影響可見性:私有和私有繼承成員的名稱是可見的,並被過載決議考慮,對不可訪問基類的隱式轉換仍然被考慮,等等。成員訪問檢查是在解釋任何給定語言結構之後的最後一步。此規則的目的是將任何 private
替換為 public
絕不會改變程式的行為。
對 預設函式實參 中以及預設 模板形參 中使用的名稱的訪問檢查是在宣告點執行的,而不是在使用點執行的。
虛擬函式 名稱的訪問規則在呼叫點使用用於表示呼叫成員函式的物件的表示式型別進行檢查。最終過載器的訪問許可權被忽略。
struct B { virtual int f(); // f is public in B }; class D : public B { private: int f(); // f is private in D }; void f() { D d; B& b = d; b.f(); // OK: B::f is public, D::f is invoked even though it's private d.f(); // error: D::f is private }
根據非限定 名稱查詢 為私有的名稱,可能可以透過限定名稱查詢進行訪問。
class A {}; class B : private A {}; class C : public B { A* p; // error: unqualified name lookup finds A as the private base of B ::A* q; // OK: qualified name lookup finds the namespace-level declaration };
透過繼承圖中的多條路徑可訪問的名稱,具有訪問許可權最高的路徑的訪問許可權。
class W { public: void f(); }; class A : private virtual W {}; class B : public virtual W {}; class C : public A, public B { void f() { W::f(); // OK: W is accessible to C through B } };
在一個類中可以出現任意數量的訪問說明符,並且可以以任何順序出現。
成員訪問說明符可能會影響 類佈局:非靜態資料成員的地址僅保證按照宣告順序增加,對於 不被訪問說明符分隔的成員(直到 C++11)具有相同訪問許可權的成員(自 C++11 起)。 |
(直至 C++23) |
對於 標準佈局型別,所有非靜態資料成員必須具有相同的訪問許可權。 |
(C++11 起) |
當在同一個類中重新宣告成員時,它必須在相同的成員訪問許可權下進行。
struct S { class A; // S::A is public private: class A {}; // error: cannot change access };
[編輯] 公有成員訪問
公有成員構成類公共介面的一部分(公共介面的其他部分是透過 ADL 找到的非成員函式)。
類的公有成員可以在任何地方訪問。
class S { public: // n, E, A, B, C, U, f are public members int n; enum E {A, B, C}; struct U {}; static void f() {} }; int main() { S::f(); // S::f is accessible in main S s; s.n = S::B; // S::n and S::B are accessible in main S::U x; // S::U is accessible in main }
[編輯] 保護成員訪問
保護成員構成類對其派生類的介面(這與類的公共介面不同)。
類的保護成員僅可訪問:
struct Base { protected: int i; private: void g(Base& b, struct Derived& d); }; struct Derived : Base { friend void h(Base& b, Derived& d); void f(Base& b, Derived& d) // member function of a derived class { ++d.i; // OK: the type of d is Derived ++i; // OK: the type of the implied '*this' is Derived // ++b.i; // error: can't access a protected member through // Base (otherwise it would be possible to change // other derived classes, like a hypothetical // Derived2, base implementation) } }; void Base::g(Base& b, Derived& d) // member function of Base { ++i; // OK ++b.i; // OK ++d.i; // OK } void h(Base& b, Derived& d) // Friend of Derived { ++d.i; // OK: friend of Derived can access a protected // member through an object of Derived // ++b.i; // error: friend of Derived is not a friend of Base } void x(Base& b, Derived& d) // non-member non-friend { // ++b.i; // error: no access from non-member // ++d.i; // error: no access from non-member }
當形成指向保護成員的指標時,它必須在其宣告中使用派生類。
struct Base { protected: int i; }; struct Derived : Base { void f() { // int Base::* ptr = &Base::i; // error: must name using Derived int Base::* ptr = &Derived::i; // OK } };
[編輯] 私有成員訪問
私有成員構成類的實現,以及類其他成員的私有介面。
類的私有成員僅可訪問該類的成員和友元,無論這些成員是在相同還是不同的例項中。
class S { private: int n; // S::n is private public: S() : n(10) {} // this->n is accessible in S::S S(const S& other) : n(other.n) {} // other.n is accessible in S::S };
顯式轉換(C 風格和函式風格)允許將派生左值轉換為其私有基類的引用,或將指向派生類的指標轉換為指向其私有基類的指標。
[編輯] 繼承
有關公有、保護和私有繼承的含義,請參見 派生類。
[編輯] 關鍵字
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 1873 | C++98 | 保護成員可被派生類的友元訪問 | 變為不可訪問 |