プライベート、パブリック、および保護された継承の違い


回答:


1065

その質問にお答えするために、まずメンバーのアクセサについて私自身の言葉で説明したいと思います。これをすでに知っている場合は、見出し「next:」にスキップしてください。

そこ私の知る3アクセサは以下の通りですpublicprotectedprivate

みましょう:

class Base {
    public:
        int publicMember;
    protected:
        int protectedMember;
    private:
        int privateMember;
};
  • 認識しているすべてのものはBase、をBase含むことも認識していますpublicMember
  • 子供(およびその子供)のみがをBase含むことを認識していますprotectedMember
  • 誰ませんが、Base認識していますprivateMember

「認識している」とは、「存在を認識し、アクセスできる」ことを意味します。

次:

パブリック、プライベート、および保護された継承でも同じことが起こります。クラスBaseと、Childから継承するクラスを考えてみましょうBase

  • 継承がある場合はpublic、認識しているすべてのものBaseChildもそれを認識しているChild継承からBase
  • 継承がprotectedである場合Child、とその子だけが、継承元であることを認識していBaseます。
  • 継承がの場合、継承を知っているprivate人は他にいませんChild

183
C ++での可視性がオブジェクトではなくクラスに基づいているということを少し補足します。つまり、同じクラスのオブジェクトは、制限なしに互いのプライベートフィールドにアクセスできます。
Zhe Chen

48
これを理解するのが難しい場合は、Kirill V. Lyadvinskyの回答を読んでから、戻ってこれを読んでください。
Vivandiere、2015年

6
これは、ほとんどの場合、からの継承SomeBaseがtypeの匿名メンバーを構成するためのハードコーディングされた方法と同じように機能するもう1つのケースですSomeBase。これは、他のメンバーと同様に、外部アクセスに対して同じ制御を行うアクセス指定子を持っています。
underscore_d

1
@ZheChenプライベートフィールドの年齢を持つクラスPersonのトムとジェリーのオブジェクトがある場合、トムを使用してジェリーの年齢にどのようにアクセス(および変更)しますか?
2016

2
「「継承」を知っている」とはどういう意味ですか?「これにはアクセスできますが、それにはアクセスできません」ということは理解できますが、「AはBから継承していることを知っています」と言われても理解できません。ここで何をしているのですか?
neilxdims 2018

1458
class A 
{
public:
    int x;
protected:
    int y;
private:
    int z;
};

class B : public A
{
    // x is public
    // y is protected
    // z is not accessible from B
};

class C : protected A
{
    // x is protected
    // y is protected
    // z is not accessible from C
};

class D : private A    // 'private' is default for classes
{
    // x is private
    // y is private
    // z is not accessible from D
};

重要な注意:クラスB、C、Dにはすべて変数x、y、zが含まれています。アクセスの問題です。

保護された継承とプライベート継承の使用法については、こちらをご覧ください


35
アンズリオが書いたものは、すぐ下の回答と組み合わせてクリックしただけです。1.лус1.
Iwillnotexist Idonotexist 2015年

2
これがどのように機能するかについての私の理解はとても遠いものでした!明確にしていただきありがとうございます。
tjwrona1992

これを理解するのに少し時間がかかりました。しかし、今は明らかです。ありがとう!
Chan Kim

115

継承の可視性を制限すると、一部のクラスが別のクラスを継承していることがコードで認識できstatic_castなくなります。派生からベースへの暗黙的な変換は機能せず、ベースから派生への暗黙的な変換も機能しません。

クラスのメンバー/友達だけがプライベート継承を見ることができ、メンバー/友達と派生クラスだけが保護された継承を見ることができます。

公的遺産

  1. IS-A継承。ボタンはウィンドウであり、ウィンドウが必要な場所ならどこでもボタンを渡すことができます。

    class button : public window { };

保護された継承

  1. 期間内に保護されます。めったに役に立ちません。boost::compressed_pair空のクラスから派生し、空の基本クラスの最適化を使用してメモリを節約するために使用されます(以下の例では、ポイントを維持するためにテンプレートを使用していません)。

    struct empty_pair_impl : protected empty_class_1 
    { non_empty_class_2 second; };
    
    struct pair : private empty_pair_impl {
      non_empty_class_2 &second() {
        return this->second;
      }
    
      empty_class_1 &first() {
        return *this; // notice we return *this!
      }
    };

個人相続

  1. 条件付きで実装。基本クラスの使用法は、派生クラスを実装するためだけのものです。トレイトとサイズが重要な場合に役立ちます(関数のみを含む空のトレイトは、空の基本クラスの最適化を利用します)。多くの場合、封じ込めがより良い解決策です。文字列のサイズは重要なので、ここでよく見られる使用法です

    template<typename StorageModel>
    struct string : private StorageModel {
    public:
      void realloc() {
        // uses inherited function
        StorageModel::realloc();
      }
    };

会員

  1. 集計

    class pair {
    public:
      First first;
      Second second;
    };
  2. アクセサ

    class window {
    public:
        int getWidth() const;
    };

保護されたメンバー

  1. 派生クラスへの拡張アクセスの提供

    class stack {
    protected:
      vector<element> c;
    };
    
    class window {
    protected:
      void registerClass(window_descriptor w);
    };

プライベートメンバー

  1. 実装の詳細を保持する

    class window {
    private:
      int width;
    };

Cスタイルのキャストでは、意図的に派生クラスを定義済みの安全な方法で保護されたまたはプライベートの基本クラスにキャストし、他の方向にもキャストできることに注意してください。コードを実装の詳細に依存させる可能性があるため、これは絶対に避けてください。ただし、必要に応じて、この手法を利用できます。


7
スコット・マイヤーズ(私が彼のものと同じくらい)は、一般的な混乱に対して多くの答えを持っていると思います。私は今、彼のIS-AとIS-IMPLEMENTED-IN-TERMS-OFの類似性は、何が起こっているのかで十分だと思います。
DangerMouse 2012

65

これら3つのキーワードは、完全に異なるコンテキストで使用され、可視性継承モデルを指定します。

この表は、サブクラスが完全に定義された場合にコンポーネントへのアクセスを示すコンポーネント宣言と継承モデルの可能な組み合わせをすべてまとめたものです。

ここに画像の説明を入力してください

上記の表は次のように解釈されます(最初の行を見てください)。

コンポーネントがpublicとして宣言され、そのクラスがpublicとして継承される場合、結果のアクセスpublicになります

例:

 class Super {
    public:      int p;
    private:     int q;
    protected:   int r;
 };

 class Sub : private Super {};

 class Subsub : public Sub {};

変数の結果のアクセスpqrクラスのSubsubはありませんなし

もう一つの例:

class Super {
    private:     int x;
    protected:   int y;
    public:      int z;
 };
class Sub : protected Super {};

変数について得られたアクセスyzクラス内のサブされる保護及び変数のxではないなし

より詳細な例:

class Super {
private:
    int storage;
public:
    void put(int val) { storage = val;  }
    int  get(void)    { return storage; }
};
int main(void) {
    Super object;

    object.put(100);
    object.put(object.get());
    cout << object.get() << endl;
    return 0;
}

次に、サブクラスを定義します。

class Sub : Super { };

int main(void) {
    Sub object;

    object.put(100);
    object.put(object.get());
    cout << object.get() << endl;
    return 0;
}

という名前のクラスのサブクラスであるSubという名前の定義済みクラス、SuperまたはそのSubクラスはSuperクラスから派生しています。このSubクラスは、新しい変数も新しい関数も導入していません。クラスのオブジェクトがクラスのオブジェクトのコピーになったSub後、Superクラスのオブジェクトがすべての特性を継承するということSuperですか?

いいえ。そうではありません。

次のコードをコンパイルするputと、getメソッドにアクセスできないというエラーメッセージが表示されます。どうして?

可視性指定子を省略すると、コンパイラーはいわゆるプライベート継承を適用するものと想定します。つまり、すべてのパブリックスーパークラスコンポーネントはプライベートアクセスに変わり、プライベートスーパークラスコンポーネントにはまったくアクセスできなくなります。したがって、サブクラス内で後者を使用することはできません。

以前に使用したアクセスポリシーを保持することをコンパイラに通知する必要があります。

class Sub : public Super { };

誤解しないでください。それは、スーパークラスのプライベートコンポーネント(ストレージ変数など)が多少不思議な方法でパブリックコンポーネントになることを意味しません。プライベートコンポーネントが残る民間公共の ままになります公共

Subクラスのオブジェクトは、クラスから作成された古い兄弟と「ほぼ」同じことを行う場合がありますSuper「ほぼ」というのは、サブクラスであるという事実は、クラスがスーパークラスのプライベートコンポーネントにアクセスできなくなったことも意味しますSubストレージ変数を直接操作できるクラスのメンバー関数を記述することはできません。

これは非常に深刻な制限です。回避策はありますか?

はい

3番目のアクセスレベルは保護と呼ばます。保護されたキーワードは、それでマークされたコンポーネントがサブクラスのいずれかで使用された場合にパブリックコンポーネントのよう動作し、残りの部分ではプライベートコンポーネントのように見えることを意味します。- これは、パブリックに継承されたクラス(この例のスーパークラスなど)にのみ当てはまります -

class Super {
protected:
    int storage;
public:
    void put(int val) { storage = val;  }
    int  get(void)    { return storage; }
};

class Sub : public Super {
public:
    void print(void) {cout << "storage = " << storage;}
};

int main(void) {
    Sub object;

    object.put(100);
    object.put(object.get() + 1);
    object.print();
    return 0;
}

例のコードでわかるように、Subクラスに新しい機能があり、それは1つの重要なことを実行しますスーパークラスからストレージ変数にアクセスします

変数がプライベートとして宣言されている場合、それは不可能です。メイン関数スコープでは、変数はとにかく隠されたままなので、次のように記述した場合:

object.storage = 0;

コンパイラはそれがであることを通知しますerror: 'int Super::storage' is protected

最後に、最後のプログラムは次の出力を生成します。

storage = 101

4
(Class:SuperClassのように)修飾子の欠如に言及する最初の1つはプライベートをもたらします。これは、徹底した説明とともに、他の人が見逃している重要な部分です。+1

2
IMOはやりすぎですが、最初はテーブルが好きです。
cp.engr

63

これは、基本クラスのパブリックメンバーが派生クラスから公開される方法に関係しています。

  • public->基本クラスのパブリックメンバーはパブリックになります(通常はデフォルト)
  • 保護->基本クラスのパブリックメンバーは保護されます
  • プライベート->基本クラスのパブリックメンバーはプライベートになります

litbが指摘するように、パブリック継承は、ほとんどのプログラミング言語で見られる従来の継承です。つまり、「IS-A」関係をモデル化しています。プライベート継承は、C ++に固有のAFAIKのようなもので、「条件付きで実装された」関係です。つまり、派生クラスでパブリックインターフェイスを使用したいが、派生クラスのユーザーがそのインターフェイスにアクセスできないようにする必要があります。この場合、基本クラスを集約する必要があると多くの人が主張しています。つまり、基本クラスをプライベートベースとして持つ代わりに、基本クラスの機能を再利用するために、派生クラスのメンバーを作成します。


13
「公開:継承はすべての人に見える」と言ったほうがいいでしょう。保護されている:継承は派生クラスと友達だけが見ることができます "、"プライベート:継承はクラス自体と友達だけが見ることができます "これは、メンバーだけが見えないだけでなく、 IS-A関係は見えない場合があります
Johannes Schaub-litb 2009年

4
私がプライベート継承を使用したのは、Doug Tが説明したとおりです。つまり、「派生クラスでパブリックインターフェイスを使用したいが、派生クラスのユーザーにそのインターフェイスへのアクセスを許可したくない」ということです。私は基本的に、古いインターフェイスを封印し、派生クラスを通じて別のインターフェイスを公開するために使用しました。
リッチ

36
Member in base class : Private   Protected   Public   

継承タイプ :              オブジェクトは次のように継承されます:

Private            :   Inaccessible   Private     Private   
Protected          :   Inaccessible   Protected   Protected  
Public             :   Inaccessible   Protected   Public

23
この誤解を招く。基本クラスのプライベートメンバーは、通常のプライベートクラスメンバーとはまったく異なる動作をします。派生クラスからはまったくアクセスできません。「プライベート」の3つの列は「アクセス不可」の列になると思います。この質問に対するKirill V. Lyadvinskyの回答を参照してください。
Sam Kauffman 2013

27

1)公的継承

a。Baseクラスのプライベートメンバーは、Derivedクラスではアクセスできません。

b。Baseクラスの保護されたメンバーは、Derivedクラスで保護されたままです。

c。Baseクラスのパブリックメンバーは、Derivedクラスでパブリックのままです。

したがって、他のクラスは、Derivedクラスオブジェクトを介してBaseクラスのパブリックメンバーを使用できます。

2)保護された継承

a。Baseクラスのプライベートメンバーは、Derivedクラスではアクセスできません。

b。Baseクラスの保護されたメンバーは、Derivedクラスで保護されたままです。

c。BaseクラスのパブリックメンバーもDerivedクラスの保護されたメンバーになります。

したがって、他のクラスは、Derivedクラスオブジェクトを介してBaseクラスのパブリックメンバーを使用できません。ただし、それらはDerivedのサブクラスで使用できます。

3)プライベート継承

a。Baseクラスのプライベートメンバーは、Derivedクラスではアクセスできません。

b。Baseクラスの保護メンバーとパブリックメンバーは、Derivedクラスのプライベートメンバーになります。

そのため、Baseクラスのメンバーは、Derivedクラスでプライベートであるため、Derivedクラスオブジェクトを介して他のクラスからアクセスできません。そのため、Derivedクラスのサブクラスでもアクセスできません。


20

パブリック継承はIS-A関係をモデル化します。と

class B {};
class D : public B {};

すべてD Bです。

プライベート継承は、IS-IMPLEMENTED-USING関係(またはそれと呼ばれるもの)をモデル化します。と

class B {};
class D : private B {};

a DはではありませんB、実装でDはすべてを使用Bします。代わりに包含を使用することにより、プライベート継承を常に排除できます。

class B {};
class D {
  private: 
    B b_;
};

これDも、を使用してB、この場合はを使用して実装できますb_。封じ込めは、継承よりもタイプ間の緊密な結合ではないため、一般的には優先する必要があります。プライベート継承の代わりに包含を使用することは、プライベート継承ほど便利ではない場合があります。多くの場合、それは怠惰であることの怠惰な言い訳です。

誰もがprotected継承モデルを知っているとは思いません。少なくとも説得力のある説明はまだ見ていません。


一部は関係として言う。ハンマーとして椅子を使用するように。ここの椅子:保護されたハンマー
user4951 2013年

プライベート継承の代わりにコンテインメントを使用すると、プライベート継承ほど便利ではありませんか?例を挙げて説明してもらえますか?
デストラクタ

@Pravasi:Dからプライベートに派生した場合D、の仮想関数をオーバーライドできますB。(たとえば、Bオブザーバーインターフェイスの場合、それDを実装して、this誰もがDオブザーバーとして使用することなく、多くのインターフェイスを必要とする関数に渡すことができます。)また、を実行するDことにより、のメンバーを選択的Bにそのインターフェイスで使用できるようにすることができますusing B::member。どちらがBメンバーであるかを実装するのは構文的に不便です。
sbi 2015年

@sbi:古いものですが... CRTPや仮想の場合、封じ込めは不可です(コメントで正しく説明されているように-しかし、これは、Bに抽象メソッドがあり、あなたが含まれている場合、封じ込めとしてモデル化できないことを意味しますそれに触れることはできません)。protected継承はvirtual基本クラスとprotectedctorで便利だとわかりました:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
lorro

11

別のクラスからパブリックに継承した場合、誰もがあなたが継承していることを知っているので、基本クラスポインターを介して誰でも多態的に使用できます。

保護された方法で継承すると、子クラスだけが多態的に使用できます。

プライベートに継承した場合、自分だけが親クラスのメソッドを実行できます。

これは基本的に、親クラスとの関係について残りのクラスが持つ知識を象徴しています


9

保護されたデータメンバーは、クラスから継承するすべてのクラスからアクセスできます。ただし、プライベートデータメンバーはできません。次のものがあるとします。

class MyClass {
    private:
        int myPrivateMember;    // lol
    protected:
        int myProtectedMember;
};

拡張機能内からこのクラスへの参照this.myPrivateMemberは機能しません。ただし、そうしthis.myProtectedMemberます。値は、まだ我々が呼ばれるこのクラスのインスタンス化を持っている場合ので、カプセル化されmyObj、その後、myObj.myProtectedMemberそれはプライベートなデータメンバーと機能が似ている、機能しません。


8
Accessors    | Base Class | Derived Class | World
—————————————+————————————+———————————————+———————
public       |      y     |       y       |   y
—————————————+————————————+———————————————+———————
protected    |      y     |       y       |   n
—————————————+————————————+———————————————+———————
private      |            |               |    
  or         |      y     |       n       |   n
no accessor  |            |               |

y: accessible
n: not accessible

この Javaの例に基づいて...千語に値する小さなテーブルだと思います:)


Javaは唯一の公共の継承を持っている
Zelldon

これはjavaについて話すトピックではなく、いいえ、あなたは間違っています...詳細については、上記の私の回答のリンクをたどってください
Enissay

あなたはjavaについて言及したので、それがトピックです。そして、あなたの例は、jacaで使用する指定子を処理します。問題は、Javaには存在しない継承の指定子についてであり、違いがあります。スーパークラスのフィールドがパブリックで継承がプライベートの場合、フィールドはサブクラス内でのみアクセスできます。外部では、サブクラスがスーパークラスを拡張するかどうかは示されません。ただし、この表ではフィールドとメソッドの指定子のみを説明しています。
ゼルドン、2015年

7

概要:

  • プライベート:クラス内以外は誰も見ることができません
  • 保護:プライベート+派生クラスはそれを見ることができます
  • パブリック:世界はそれを見ることができます

継承するとき、(一部の言語では)データメンバーの保護タイプを特定の方向に、たとえば保護からパブリックに変更できます。


6

民間:

基本クラスのプライベートメンバーには、その基本クラスのメンバーだけがアクセスできます。

公衆:

基本クラスのパブリックメンバーには、その基本クラスのメンバー、その派生クラスのメンバー、および基本クラスと派生クラスの外部にあるメンバーがアクセスできます。

保護されています:

基本クラスの保護されたメンバーには、基本クラスのメンバーだけでなく、その派生クラスのメンバーもアクセスできます。


要するに:

プライベート:ベース

保護:ベース+派生

public:ベース+派生+その他のメンバー


5

私は簡単な答えを見つけたので、将来の参考のために投稿することも考えました。

リンクhttp://www.learncpp.com/cpp-tutorial/115-inheritance-and-access-specifiers/から

class Base
{
public:
    int m_nPublic; // can be accessed by anybody
private:
    int m_nPrivate; // can only be accessed by Base member functions (but not derived classes)
protected:
    int m_nProtected; // can be accessed by Base member functions, or derived classes.
};

class Derived: public Base
{
public:
    Derived()
    {
        // Derived's access to Base members is not influenced by the type of inheritance used,
        // so the following is always true:

        m_nPublic = 1; // allowed: can access public base members from derived class
        m_nPrivate = 2; // not allowed: can not access private base members from derived class
        m_nProtected = 3; // allowed: can access protected base members from derived class
    }
};

int main()
{
    Base cBase;
    cBase.m_nPublic = 1; // allowed: can access public members from outside class
    cBase.m_nPrivate = 2; // not allowed: can not access private members from outside class
    cBase.m_nProtected = 3; // not allowed: can not access protected members from outside class
}

3

これは基本的に、派生クラスの基本クラスのパブリックおよび保護されたメンバーのアクセス保護です。パブリック継承により、派生クラスはベースのパブリックメンバーと保護されたメンバーを見ることができます。プライベート継承ではできません。保護されている場合、派生クラスとその派生クラスはそれらを見ることができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.