Access specifiers
class/struct または union の member-specification において、後続のメンバーのアクセシビリティを定義します。
派生クラス の宣言における base-specifier で、後続の基底クラスの継承メンバーのアクセシビリティを定義します。
目次 |
構文
public
:
メンバー宣言
|
(1) | ||||||||
protected
:
メンバー宣言
|
(2) | ||||||||
private
:
メンバー宣言
|
(3) | ||||||||
| public 基底クラス | (4) | ||||||||
| protected 基底クラス | (5) | ||||||||
| private 基底クラス | (6) | ||||||||
基底クラスのprivateメンバは、public、protected、privateのいずれの継承であっても、常に派生クラスからアクセスできません。
説明
すべての class メンバー(静的、非静的、関数、型など)の名前には、関連する「メンバーアクセス」があります。プログラム内でメンバーの名前が使用される際、そのアクセスがチェックされ、アクセス規則を満たさない場合、プログラムはコンパイルされません:
#include <iostream> class Example { public: // この後のすべての宣言はpublic void add(int x) // メンバー関数「add」はpublicアクセス { n += x; // OK: privateメンバーExample::nはExample::addからアクセス可能 } private: // この後のすべての宣言はprivate int n = 0; // メンバー変数「n」はprivateアクセス }; int main() { Example e; e.add(1); // OK: publicメンバーExample::addはmainからアクセス可能 // e.n = 7; // エラー: privateメンバーExample::nはmainからアクセス不可 }
アクセス指定子は、クラスの作成者に対して、クラスの利用者(つまり interface )がアクセスできるクラスメンバーと、クラスの内部使用( implementation )のためのメンバーを決定する能力を与えます。
詳細
クラスのすべてのメンバー( member functions の本体、メンバーオブジェクトの初期化子、および nested class definitions 全体)は、そのクラスがアクセス可能なすべての名前にアクセスできます。メンバー関数内のローカルクラスは、そのメンバー関数がアクセス可能なすべての名前にアクセスできます。
class
キーワードで定義されたクラスは、デフォルトでそのメンバと基底クラスに対してprivateアクセスを持ちます。
struct
キーワードで定義されたクラスは、デフォルトでそのメンバと基底クラスに対してpublicアクセスを持ちます。
union
は、デフォルトでそのメンバに対してpublicアクセスを持ちます。
保護されたメンバーまたは非公開メンバーへの追加機能やクラスへのアクセス権を付与するには、 フレンド宣言 を使用できます。
アクセシビリティはその起源に関係なくすべての名前に適用されるため、 typedef または using declarations (継承コンストラクタを除く)によって導入された名前がチェックされ、それが参照する名前はチェックされません:
class A : X { class B {}; // BはA内でprivate public: typedef B BB; // BBはpublic }; void f() { A::B y; // エラー: A::Bはprivate A::BB x; // OK: A::BBはpublic }
メンバーアクセスは可視性に影響を与えません:privateおよび非公開継承されたメンバーの名前は可視であり、オーバーロード解決で考慮され、アクセス不可能な基底クラスへの暗黙変換は依然として考慮される、など。メンバーアクセスチェックは、任意の言語構成が解釈された後の最終ステップです。この規則の意図は、
private
を
public
に置き換えてもプログラムの動作が決して変わらないことです。
デフォルト関数引数で使用される名前のアクセスチェックは、 デフォルト関数引数 およびデフォルト テンプレートパラメータ で使用される名前のアクセスチェックは、使用時点ではなく宣言時点で実行されます。
virtual functions の名前に対するアクセス規則は、メンバ関数が呼び出されるオブジェクトを表す式の型を使用して、呼び出しポイントでチェックされます。最終オーバーライダのアクセス権は無視されます:
struct B { virtual int f(); // Bではfはpublic }; class D : public B { private: int f(); // Dではfはprivate }; void f() { D d; B& b = d; b.f(); // OK: B::fはpublic、D::fがprivateであっても呼び出される d.f(); // エラー: D::fはprivate }
修飾なしの name lookup によって非公開とされる名前は、修飾された名前探索を通じてアクセス可能である場合があります:
class A {}; class B : private A {}; class C : public B { A* p; // エラー: 非修飾名検索がBのprivate基底としてAを発見 ::A* q; // OK: 修飾名検索が名前空間レベルの宣言を発見 };
継承グラフ内で複数のパスを通じてアクセス可能な名前は、最もアクセス権の高いパスでのアクセス権を持ちます:
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はBを通じてCからアクセス可能 } };
任意の数のアクセス指定子がクラス内に任意の順序で現れることができます。
|
メンバアクセス指定子は クラスレイアウト に影響する可能性がある:非静的データメンバのアドレスは、宣言順に増加することが保証されているのは、 アクセス指定子で区切られていないメンバ (C++11まで) 同じアクセス権を持つメンバ (C++11以降) のみである。 |
(C++23まで) |
|
standard-layout types では、すべての非静的データメンバは同じアクセス権を持たなければならない。 |
(C++11以降) |
同じクラス内でメンバーが再宣言される場合、同じメンバーアクセス権限で行わなければなりません:
struct S { class A; // S::A は public private: class A {}; // エラー: アクセス権を変更できません };
パブリックメンバーアクセス
パブリックメンバはクラスのパブリックインターフェースの一部を形成します(パブリックインターフェースの他の部分は ADL によって見つかる非メンバ関数です)。
クラスのpublicメンバはどこからでもアクセス可能です:
class S { public: // n, E, A, B, C, U, f は公開メンバーです int n; enum E {A, B, C}; struct U {}; static void f() {} }; int main() { S::f(); // S::f は main 関数内でアクセス可能です S s; s.n = S::B; // S::n と S::B は main 関数内でアクセス可能です S::U x; // S::U は main 関数内でアクセス可能です }
プロテクテッドメンバーアクセス
プロテクテッドメンバは、基底クラスから派生クラスへのインターフェースを形成します(これはクラスのパブリックインターフェースとは区別されます)。
クラスのprotectedメンバは以下の場合にのみアクセス可能です
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) // 派生クラスのメンバー関数 { ++d.i; // OK: dの型はDerived ++i; // OK: 暗黙の'*this'の型はDerived // ++b.i; // エラー: Baseを通じてprotectedメンバーにアクセスできない // (そうでなければ、仮想的なDerived2のような他の派生クラスの // 基底実装を変更することが可能になってしまう) } }; void Base::g(Base& b, Derived& d) // Baseのメンバー関数 { ++i; // OK ++b.i; // OK ++d.i; // OK } void h(Base& b, Derived& d) // Derivedのフレンド関数 { ++d.i; // OK: DerivedのフレンドはDerivedのオブジェクトを通じて // protectedメンバーにアクセス可能 // ++b.i; // エラー: DerivedのフレンドはBaseのフレンドではない } void x(Base& b, Derived& d) // 非メンバー非フレンド関数 { // ++b.i; // エラー: 非メンバーからのアクセス不可 // ++d.i; // エラー: 非メンバーからのアクセス不可 }
保護されたメンバへのポインタが形成される場合、その宣言では派生クラスを使用する必要があります:
struct Base { protected: int i; }; struct Derived : Base { void f() { // int Base::* ptr = &Base::i; // エラー: Derivedを使用して指定する必要があります int Base::* ptr = &Derived::i; // OK } };
プライベートメンバーアクセス
プライベートメンバは、クラスの実装を形成するとともに、クラスの他のメンバに対するプライベートインターフェースを提供します。
クラスのプライベートメンバーは、そのクラスのメンバーとフレンドのみがアクセス可能であり、メンバーが同じインスタンスまたは異なるインスタンスにあるかどうかに関わらず同様です:
class S { private: int n; // S::n は private public: S() : n(10) {} // this->n は S::S 内でアクセス可能 S(const S& other) : n(other.n) {} // other.n は S::S 内でアクセス可能 };
explicit cast (Cスタイルおよび関数スタイル)は、派生lvalueからそのprivate基底への参照へのキャスト、または派生へのポインタからそのprivate基底へのポインタへのキャストを許可します。
継承
derived classes におけるpublic、protected、private継承の意味については、こちらを参照してください。
キーワード
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用対象 | 公開時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 1873 | C++98 | protected members were accessible to friends of derived classes | made inaccessible |