アクセス指定子
クラス/構造体または共用体の member-specification において、後続のメンバのアクセス可能性を定義します。
派生クラスの宣言の base-specifier において、後続の基底クラスの継承メンバのアクセス可能性を定義します。
構文
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: // この地点より後のすべての宣言はパブリックです。
void add(int x) { // メンバ「add」はパブリックアクセスを持ちます。
n += x; // OK、プライベートな Example::n は Example::add からアクセスできます。
}
private: // この地点より後のすべての宣言はプライベートです。
int n = 0; // メンバ「n」はプライベートアクセスを持ちます。
};
int main()
{
Example e;
e.add(1); // OK、パブリックな Example::add は main からアクセスできます。
// e.n = 7; // エラー、プライベートな Example::n は main からアクセスできません。
}
アクセス指定子はどのクラスメンバがクラスの使用者からアクセス可能であるか (すなわちインタフェース) およびどのメンバがクラスの内部使用のためであるか (すなわち実装) を決定する能力をクラスの作者に与えます。
詳細
クラスのすべてのメンバ (メンバ関数の本体、メンバオブジェクトの初期化子、およびネストしたクラス定義全体) が、クラスがアクセスできるすべての名前へのアクセスを持ちます。 メンバ関数内のローカルクラスは、そのメンバ関数がアクセスできるすべての名前へのアクセスを持ちます。
キーワード class を用いて定義されたクラスは、そのメンバおよびその基底クラスに対して、デフォルトでプライベートアクセスを持ちます。 キーワード struct を用いて定義されたクラスは、そのメンバおよびその基底クラスに対して、デフォルトでパブリックアクセスを持ちます。 union はそのメンバに対してデフォルトでパブリックアクセスを持ちます。
プロテクテッドまたはプライベートメンバへのアクセスを追加の関数またはクラスに許可するために、フレンド宣言を使用することができます。
アクセス可能性はその起源と無関係にすべての名前に適用されるため、 typedef または using 宣言によって導入された名前はチェックされ、その参照先の名前はチェックされません。
class A : X {
class B { }; // B はプライベートです。
public:
typedef B BB; // BB はパブリックです。
};
void f() {
A::B y; // エラー、 A::B はプライベートです。
A::BB x; // OK、 A::BB はパブリックです。
}
メンバアクセスは可視性に影響しません。 プライベートなメンバおよびプライベート継承したメンバの名前は可視である、オーバーロード解決で考慮される、アクセス不可能な基底クラスへの暗黙の変換は未だ考慮される、などです。 メンバアクセスのチェックはあらゆる与えられた言語要素が解釈された後の最後のステップです。 このルールの意図は、いかなる private を public に置き換えてもプログラムの動作を決して変えないことです。
デフォルト関数引数およびデフォルトテンプレート引数で使用される名前に対するアクセスチェックは、使用の時点ではなく、宣言の時点で行われます。
仮想関数の名前に対するアクセスルールは、呼び出しの地点で、メンバ関数が呼ばれるオブジェクトを表すために使用される式の型を用いて、行われます。 最終オーバーライダーのアクセスは無視されます。
struct B { virtual int f(); }; // f は B 内ではパブリックです。
class D : public B { private: int f(); }; // f は D 内ではプライベートです。
void f() {
D d;
B& b = d;
b.f(); // OK、 B::f() はパブリックです。 たとえプライベートでも D::f() が呼ばれます。
d.f(); // エラー、 D::f() はプライベートです。
}
無修飾の名前探索ではプライベートな名前が修飾付き名前探索を通してアクセス可能である場合もあります。
class A { };
class B : private A { };
class C : public B {
A* p; // エラー、無修飾の名前探索は B のプライベートな基底としての 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 からアクセス可能です。
};
任意個のアクセス指定子が任意の順序でクラス内に現れることができます。 メンバアクセス指定子はクラスのレイアウトに影響することがあります。 非静的データメンバのアドレスは同じアクセスを持つメンバについて宣言の順序で増加することのみが保証されます。 StandardLayoutType の場合はすべての非静的データメンバが同じアクセスを持たなければなりません。
メンバが同じクラス内で再宣言されるときは、同じメンバアクセスの下で行われなければなりません。
struct S {
class A; // S::A はパブリックです。
private:
class A {}; // エラー、アクセスを変えることはできません。
};
パブリックメンバアクセス
パブリックメンバはクラスの公開インタフェースの一部を形成します (公開インタフェースの他の部分は ADL によって発見される非メンバ関数です)。
クラスのパブリックメンバはどこからでもアクセス可能です。
class S {
public: // n、f、E、A、B、C、U はパブリックメンバです。
int n;
static void f() {}
enum E {A, B, C};
struct U {};
};
int main()
{
S::f(); // S::f は main からアクセス可能です。
S s;
s.n = S::B; // S::n および S::B は main からアクセス可能です。
S::U x; // S::U は main からアクセス可能です。
}
プロテクテッドメンバアクセス
プロテクテッドメンバは派生クラスに対するインタフェースを形成します (これはクラスの公開インタフェースとは区別されます)。
クラス Base のプロテクテッドメンバは以下からのみアクセスできます。
Base のメンバおよびフレンド。Base から派生したあらゆるクラスのメンバおよびフレンド (C++17未満)。 ただし Base から派生した型のオブジェクト (this を含みます) に対する操作のときだけです。struct Base {
protected:
int i;
private:
void g(Base& b, struct Derived& d);
};
struct Derived : Base {
void f(Base& b, Derived& d) // 派生クラスのメンバ関数。
{
++d.i; // OK、 d の型は Derived です。
++i; // OK、暗黙の「*this」の型は Derived です。
// ++b.i; // エラー、 Base を通してプロテクテッドメンバにアクセスすることはできません。
// (さもなければ Derived2 のような別の派生クラスの
// 基底の実装を変更できてしまいます)
}
};
void Base::g(Base& b, Derived& d) // Base のメンバ関数。
{
++i; // OK。
++b.i; // OK。
++d.i; // OK。
}
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 はプライベートです。
public:
S() : n(10) {} // this->n は S::S からアクセス可能です。
S(const S& other) : n(other.n) {} // other.n は S::S からアクセス可能です。
};
明示的なキャスト (C スタイルおよび関数スタイル) は、派生の左辺値からそのプライベート基底の参照へのキャスト、または派生のポインタからそのプライベート基底のポインタへのキャストを許容します。
継承
パブリック継承、プロテクテッド継承、およびプライベート継承の意味については派生クラスを参照してください。