アクセス指定子
クラス/構造体または共用体のメンバー指定において、その後に続くメンバーのアクセス可能性を定義する。
派生クラス宣言の基底指定子において、その後に続く基底クラスの継承されたメンバーのアクセス可能性を定義する。
目次 |
[編集] 構文
public : member-declarations |
(1) | ||||||||
protected : member-declarations |
(2) | ||||||||
private : member-declarations |
(3) | ||||||||
| public base-class | (4) | ||||||||
| protected base-class | (5) | ||||||||
| private base-class | (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++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 1873 | C++98 | 保護されたメンバーは派生クラスのフレンドからアクセス可能だった。 | アクセス不可になった。 |