メンバー アクセス コントロール (C++)

アクセスの制御を使用すると、クラスの public インターフェイスを、private 実装の詳細や、派生クラスでのみ使用する protected メンバーから分離できます。 アクセス指定子は、その後で宣言されたすべてのメンバーに対して当てはまり、これは、次のアクセス指定子が検出されるまで続きます。

class Point
{
public:
    Point( int, int ) // Declare public constructor.;
    Point();// Declare public default constructor.
    int &x( int ); // Declare public accessor.
    int &y( int ); // Declare public accessor.

private:                 // Declare private state variables.
    int _x;
    int _y;

protected:      // Declare protected function for derived classes only.
    Point ToWindowCoords();
};

既定のアクセスは、クラスでは private、構造体または共用体では public です。 クラスのアクセス指定子は、任意の順序で何回でも使用できます。 クラス型のオブジェクトに対するストレージの割り当ては、実装に依存します。 ただし、コンパイラは、アクセス指定子間で、連続的な高いメモリ アドレスへのメンバーの割り当てを保証する必要があります。

メンバー アクセス コントロール

アクセスの種類 意味
private private として宣言されたクラス メンバーは、そのクラスのメンバー関数とフレンド (クラスまたは関数) のみが使用できます。
protected protected として宣言されたクラス メンバーは、そのクラスのメンバー関数とフレンド (クラスまたは関数) が使用できます。 また、そのクラスから派生したクラスも "protected" メンバーを使用できます。
public public として宣言されたクラス メンバーは、どの関数からでも使用できます。

アクセス制御により、不適切な方法でオブジェクトを使用するのを防ぐことができます。 明示的な型変換 (キャスト) を実行すると、この保護機能が働かなくなります。

Note

アクセス制御は、すべての名前 (メンバー関数、メンバー データ、入れ子のクラス、列挙子) に同等に適用できます。

派生クラスのアクセス コントロール

派生クラスでアクセスできる基底クラスのメンバーは、次の 2 つの要因によって制御されます。派生クラスの継承メンバーに対するアクセスも、この同じ要因によって制御されます。

  • 派生クラスが public アクセス指定子を使用して基本クラスを宣言するかどうか。

  • 基底クラスでメンバーへのアクセスがどのように指定されているか。

次の表は、これらの要因間の相互作用と、基底クラスのメンバーへのアクセスがどのように決定されるかを示しています。

基底クラスのメンバー アクセス

private protected public
派生アクセスで常にアクセスできない private 派生を使用する場合は派生クラス内で private private 派生を使用する場合は派生クラス内で private
protected 派生を使用する場合は派生クラス内で protected protected 派生を使用する場合は派生クラス内で protected
public 派生を使用する場合は派生クラス内で protected public 派生を使用する場合は派生クラス内で public

次の例は、アクセスの派生を示しています。

// access_specifiers_for_base_classes.cpp
class BaseClass
{
public:
    int PublicFunc(); // Declare a public member.
protected:
    int ProtectedFunc(); // Declare a protected member.
private:
    int PrivateFunc(); // Declare a private member.
};

// Declare two classes derived from BaseClass.
class DerivedClass1 : public BaseClass
{
    void foo()
    {
        PublicFunc();
        ProtectedFunc();
        PrivateFunc(); // function is inaccessible
    }
};

class DerivedClass2 : private BaseClass
{
    void foo()
    {
        PublicFunc();
        ProtectedFunc();
        PrivateFunc(); // function is inaccessible
    }
};

int main()
{
    DerivedClass1 derived_class1;
    DerivedClass2 derived_class2;
    derived_class1.PublicFunc();
    derived_class2.PublicFunc(); // function is inaccessible
}

DerivedClass1 では、メンバー関数 PublicFuncpublic メンバーであり、BaseClasspublic 基底クラスであるため、ProtectedFuncprotected メンバーになります。 PrivateFuncBaseClass に対して private であるため、派生クラスからはアクセスできません。

DerivedClass2 では、BaseClassprivate 基底クラスであるため、PublicFunc 関数と ProtectedFunc 関数は、private メンバーと見なされます。 繰り返しになりますが、PrivateFuncBaseClass に対して private であるため、派生クラスからはアクセスできません。

基底クラスにアクセス指定子を使用しなくても、派生クラスを宣言できます。 このような場合、派生クラスの宣言に class キーワードが使用されていれば、派生は private と見なされます。 派生クラスの宣言に struct キーワードが使用されていれば、派生は public と見なされます。 コード例を次に示します。

class Derived : Base
...

は以下に匹敵します。

class Derived : private Base
...

同様に、

struct Derived : Base
...

は以下に匹敵します。

struct Derived : public Base
...

関数や派生クラスは、基底クラスで friend 宣言を使用して宣言されていない限り、private アクセスを持つように宣言されたメンバーにアクセスできません。

union 型には基底クラスは設定できません。

Note

private 基底クラスを指定する場合は、派生クラスのユーザーにメンバー アクセスがわかるように、private キーワードを明示的に使用することをお勧めします。

アクセス制御と静的メンバー

基底クラスを private として指定した場合、非静的メンバーにのみ影響します。 パブリックな静的メンバーは、派生クラス内でもアクセスできます。 ただし、ポインター、参照、またはオブジェクトを使用して基底クラスのメンバーにアクセスするには、アクセス制御を再び適用する変換が必要になる場合があります。 次の例を考えてみましょう。

// access_control.cpp
class Base
{
public:
    int Print();             // Nonstatic member.
    static int CountOf();    // Static member.
};

// Derived1 declares Base as a private base class.
class Derived1 : private Base
{
};

// Derived2 declares Derived1 as a public base class.
class Derived2 : public Derived1
{
    int ShowCount();    // Nonstatic member.
};

// Define ShowCount function for Derived2.
int Derived2::ShowCount()
{
    // Call static member function CountOf explicitly.
    int cCount = ::Base::CountOf();     // OK.

    // Call static member function CountOf using pointer.
    cCount = this->CountOf();  // C2247: 'Base::CountOf'
                               // not accessible because
                               // 'Derived1' uses 'private'
                               // to inherit from 'Base'
    return cCount;
}

このコードでは、アクセス制御により、Derived2 へのポインターから Base へのポインターへの変換が禁止されます。 this ポインターは暗黙的に型 Derived2 * です。 CountOf 関数を選択するには、thisBase * 型に変換する必要があります。 BaseDerived2 に対する private 間接基底クラスであるため、このような変換は許可されません。 private 基底クラス型への変換は、直接の派生クラスへのポインターにのみ使用できます。 したがって、型 Derived1 * のポインターは型 Base * に変換できます。

ポインター、参照、またはオブジェクトを使用して選択することなく CountOf 関数を明示的に呼び出すことは、変換を意味しません。 したがって、呼び出しは許可されます。

派生クラス T のメンバーとフレンドは T へのポインターを、T の private 直接基底クラスへのポインターに変換できます。

仮想関数へのアクセス

virtual 関数に適用されるアクセス制御は、関数呼び出しを行う型によって決まります。 関数の宣言のオーバーライドは、特定の型のアクセス制御には影響しません。 次に例を示します。

// access_to_virtual_functions.cpp
class VFuncBase
{
public:
    virtual int GetState() { return _state; }
protected:
    int _state;
};

class VFuncDerived : public VFuncBase
{
private:
    int GetState() { return _state; }
};

int main()
{
   VFuncDerived vfd;             // Object of derived type.
   VFuncBase *pvfb = &vfd;       // Pointer to base type.
   VFuncDerived *pvfd = &vfd;    // Pointer to derived type.
   int State;

   State = pvfb->GetState();     // GetState is public.
   State = pvfd->GetState();     // C2248 error expected; GetState is private;
}

前の例では、型 VFuncBase へのポインターを使用する仮想関数 GetState の呼び出しは、VFuncDerived::GetState を呼び出します。GetStatepublic として扱われます。 ただし、GetStateVFuncDerived クラスで private として宣言されているため、VFuncDerived 型へのポインターを使用して GetState を呼び出すとアクセス制御違反になります。

注意事項

仮想関数 GetState は、基底クラス VFuncBase へのポインターを使用して呼び出すことができます。 これは、呼び出された関数がその関数の基底クラス版であることを意味しません。

多重継承でのアクセス制御

仮想基底クラスを含む多重継承のグリッドでは、任意の名前に複数のパスを通じてアクセスできます。 パスが異なると適用できるアクセス制御も異なるため、コンパイラは最大のアクセスを許可するパスを選択します。 次の図を参照してください。

Diagram showing access along the paths of an inheritance graph.

この図は、次の継承階層を示しています。クラス VBase は基底クラスです。 LeftPath クラスは、仮想 private VBase を使用して VBase から継承します。 クラス RightPath も VBase から継承しますが、仮想 public VBase を使用します。 最後に、クラス Derived は、LeftPath、RightPath を使用して public 、クラス LeftPath とクラス RightPath public の両方から継承します。

継承グラフのパスに沿ったアクセス

図では、クラス VBase で宣言される名前は、必ずクラス RightPath 経由でアクセスされます。 LeftPathVBaseprivate 基底クラスとして宣言するのに対し、RightPathVBasepublic 基底クラスとして宣言するため、右のパスの方がアクセスが簡単です。

関連項目

C++ 言語リファレンス