web-dev-qa-db-ja.com

プライベート、パブリック、プロテクトの継承の違い

C++におけるpublicprivate、およびprotectedの継承の違いは何ですか?私がSOで見つけたすべての質問は特定のケースを扱います。

912
user106599

その質問に答えるために、私は自分の言葉で最初にメンバーのアクセサについて説明したいと思います。あなたがすでにこれを知っているならば、見出し "next:"へスキップしてください。

私が知っている3つのアクセサがあります:publicprotectedprivate

みましょう:

class Base {
    public:
        int publicMember;
    protected:
        int protectedMember;
    private:
        int privateMember;
};
  • Baseを知っていることはすべてBasepublicMemberを含んでいることも知っています。
  • BaseprotectedMemberが含まれていることを知っているのは、子供たち(およびその子供たち)だけです。
  • Base以外の誰もがprivateMemberを知っていません。

「知っている」とは、「その存在を認め、アクセスできる」という意味です。

次:

同じことが公的、私的および保護された継承でも起こります。 BaseクラスとChildから継承するクラスBaseを考えてみましょう。

  • 継承がpublicの場合、BaseChildを認識しているすべてのものは、ChildBaseから継承していることも認識しています。
  • 継承がprotectedである場合、Childとその子だけが、それらがBaseから継承することを認識しています。
  • 継承がprivateの場合、Child以外の誰も継承を認識していません。
977
Anzurio
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を含んでいます。ただアクセスの問題です。

保護された継承と私的な継承の使い方については here を読むことができます。

1337

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

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

public継承

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

    class button : public window { };
    

protectedの継承

  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!
      }
    };
    

private継承

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

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

public member

  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);
    };
    

privateメンバー

  1. 実装の詳細を守る

    class window {
    private:
      int width;
    };
    

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

105

基本クラスのパブリックメンバーが派生クラスからどのように公開されるかと関係があります。

  • public - >基本クラスの公開メンバーは公開されます(通常はデフォルト)
  • protected - >基本クラスの公開メンバーは保護されます
  • 非公開 - >基本クラスの公開メンバーは非公開になります

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

62
Doug T.

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

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

enter image description here

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

コンポーネントが宣言済み as publicで、そのクラスが継承 as publicの場合結果のアクセスpublicです。

例:

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

 class Sub : private Super {};

 class Subsub : public Sub {};

クラスSubsub内の変数pqrに対するアクセス結果は、noneです。

もう一つの例:

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

クラスSub内の変数yzに対するアクセス結果はprotectedであり、変数xに対するアクセス結果はnoneです。

より詳細な例:

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メソッドにアクセスできないというコンパイルエラーが発生するだけです。どうして?

可視性指定子を省略すると、コンパイラはいわゆるprivate inheritanceを適用しようとしていると見なします。つまり、すべてのpublicスーパークラスコンポーネントがprivate accessになり、プライベートスーパークラスコンポーネントにはまったくアクセスできなくなります。したがって、サブクラス内で後者を使用することは許可されていません。

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

class Sub : public Super { };

誤解しないでください :それは、スーパークラスのプライベートコンポーネント(ストレージ変数のような)がやや不思議な方法でパブリックコンポーネントに変わることを意味するのではありません。 Privateコンポーネントはprivateのままになり、publicpublicのままになります。

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

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

はい

3番目のアクセスレベルはprotectedです。キーワードprotectedは、それがマークされたコンポーネントが{いずれかのサブクラスによって使用される場合はパブリックなもののように振る舞い、その他の国々にとってはプライベートなもののように見えるを意味します。 - これは公的に継承されたクラスにのみ当てはまります(この例のSuperクラスのように) -

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つの重要なことをします:それはSuperクラスからストレージ変数にアクセスします

変数が非公開として宣言されている場合は不可能です。メインの関数スコープでは、とにかく変数は隠されたままなので、次のように書くと

object.storage = 0;

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

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

storage = 101
57
BugShotGG
Member in base class : Private   Protected   Public   

継承タイプ - として継承されたオブジェクト

Private            :   Inaccessible   Private     Private   
Protected          :   Inaccessible   Protected   Protected  
Public             :   Inaccessible   Protected   Public
35
kinshuk4

1)公の継承

a。基本クラスの非公開メンバーは、派生クラスではアクセスできません。

b。基本クラスの保護されたメンバーは、派生クラスでも保護されたままです。

c。 Baseクラスの公開メンバーはDerivedクラスで公開されたままです。

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

2)保護された継承

a。基本クラスの非公開メンバーは、派生クラスではアクセスできません。

b。基本クラスの保護されたメンバーは、派生クラスでも保護されたままです。

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

したがって、他のクラスは、派生クラスオブジェクトを介してBaseクラスのパブリックメンバーを使用することはできません。しかし、それらはDerivedのサブクラスに利用可能です。

3)私的継承

a。基本クラスの非公開メンバーは、派生クラスではアクセスできません。

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

したがって、Baseクラスのメンバーは、派生クラス内ではプライベートなので、派生クラスオブジェクトを介して他のクラスからアクセスすることはできません。そのため、派生クラスのサブクラスでもアクセスできません。

24
yuvi

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

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

すべてのDBです。

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

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

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

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

このDBを使って、この場合はb_を使って実装することができます。包含は継承より型間の密接な結びつきが少ないので、一般的には優先されるべきです。プライベート継承の代わりに包含を使用することはプライベート継承ほど便利ではないことがあります。多くの場合、それは怠惰であるための怠け者の言い訳です。

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

19
sbi

あなたが他のクラスから公に継承するならば、誰もがあなたが継承していることを知っています、そしてあなたはベースクラスポインタを通して誰でも多態的に使用することができます。

保護的に継承した場合、あなたの子供クラスだけがあなたを多態的に使うことができます。

あなたが個人的に継承した場合、あなただけが親クラスのメソッドを実行することができます。

これは基本的に、他のクラスがあなたの親クラスとのあなたの関係について持っている知識を象徴しています。

12
Arkaitz Jimenez

保護されたデータメンバーは、あなたのクラスから継承したどのクラスからもアクセスできます。ただし、個人データメンバーはできません。次のようになっているとしましょう。

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

あなたのエクステンションの中からこのクラスまで、this.myPrivateMemberの参照は機能しません。しかし、this.myProtectedMemberはそうなります。値はまだカプセル化されているので、myObjと呼ばれるこのクラスのインスタンス化があると、myObj.myProtectedMemberは機能しません。そのため、プライベートデータメンバと機能的には同じです。

9
Andrew Noyes
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の例...私は千語の価値がある小さなテーブルだと思います:)

7
Enissay

概要:

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

継承時には、(一部の言語では)データメンバーの保護タイプを特定の方向に変更できます。から保護されてから。

7
Roee Adler

プライベート:

基本クラスの非公開メンバーには、その基本クラスのメンバーからのみアクセスできます。

パブリック:

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

保護されています:

基本クラスの保護されたメンバーは、基本クラスのメンバーとその派生クラスのメンバーからアクセスできます。


要するに:

プライベート :base

protected :base + derived

public :base + derived +他のメンバー

6
varun

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

リンクから/ 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
}
4
Prajosh Premdas

これは基本的に、派生クラス内の基本クラスのパブリックメンバーおよび保護されたメンバーのアクセス保護です。パブリック継承では、派生クラスはベースのパブリックメンバーと保護メンバーを見ることができます。私的な継承では、それはできません。 protectedでは、派生クラスとそれから派生したクラスはそれらを見ることができます。

3
Dan Olson