継承:「A」は「B」のアクセスできないベースです


82
$ cat inheritance.cpp 
#include <iostream>

using namespace std;

class A { };
class B : private A { };

int main() {
    A* ab = new B;
}
$
$ g++ inheritance.cpp
inheritance.cpp: In function 'int main()':
inheritance.cpp:9: error: 'A' is an inaccessible base of 'B'
$

私はこのエラーを理解していません。

私が理解しているように、そしてこのチュートリアルが確認しているように、private継承はメンバーがclass B外の世界に見える方法を変えるだけです。

プライベート指定子は、class Bここでメンバーの可視性を変更するだけではないと思います。

  • このエラーはどうすればよいですか?それはどういう意味ですか?
  • 基本的に、C ++でこのタイプのコードを許可することの何が問題になっていますか?完全に無害に見えます。

回答:


100

継承をプライベートにすることで、基本的に、BがAから継承するという事実でさえ(まったく)プライベートであり、外部からアクセス/表示することはできません。

それが許可された場合に何が起こるかについての長い議論に入ることなく、単純な事実はそれが許可されていないということです。ベースへのポインタを使用して派生型のオブジェクトを参照する場合は、パブリック継承の使用にかなり悩まされています。

プライベート継承は必ずしも(または通常でも)リスコフの置換原則に従うことを意図しているわけではありません。パブリック継承は、派生オブジェクトを基本クラスのオブジェクトに置き換えることができ、適切なセマンティクス引き続き得られることを主張します。しかし、私的継承はそれを主張しませ。プライベート継承によって暗示される関係の通常の説明は、「の観点から実装されます」です。

パブリック継承とは、派生クラスが基本クラスのすべての機能を維持し、さらに追加する可能性があることを意味します。プライベート継承は、多かれ少なかれ反対のことを意味することがよくあります。つまり、派生クラスは一般的な基本クラスを使用して、より制限されたインターフェイスで何かを実装します。

たとえば、今のところ、C ++標準ライブラリのコンテナがテンプレートではなく継承を使用して実装されていると仮定しましょう。現在のシステムではstd::dequestd::vectorはコンテナでありstd::stack、より制限されたインターフェイスを提供するコンテナアダプタです。テンプレートに基づいているstd::stackため、std::dequeまたはのいずれかのアダプタとして使用できますstd::vector

継承で本質的に同じものを提供したい場合は、おそらくプライベート継承を使用するので、次のようにstd::stackなります。

class stack : private vector {
    // ...
};

この場合、我々は間違いないではないユーザーが、私たちを操作できるようにしたいstack、それがであるかのようにvector。そうすることで、スタックの期待に違反する可能性があります(たとえば、ユーザーは、意図したとおりの純粋なスタックのような方法ではなく、中央でアイテムを挿入/削除できます)。基本的にvectorスタックを実装するための便利な方法として使用していますが、(たとえば)stackスタンドアロンの実装を変更した場合(基本クラスに依存しない)、またはの観点から再実装した場合std::deque、それは望ましくありませんクライアントコードに影響を与えるために-クライアントコードにとって、これは単なるスタックであり、特殊な種類のベクトル(または両端キュー)ではありません。


1
これは以下にも当てはまりますprotected
SubMachine 2010年

12

プライベート継承は、クラスBのメンバーが外の世界に見える方法を変更するだけです。

します。で、もし

A* p = new B;

許可された場合は、をB作成するだけで、継承された任意のメンバーに外部からアクセスできますA*。それらは個人的に継承されているため、そのアクセスは違法であり、アップキャストも違法です。


8

clang++ もう少しわかりやすいエラーメッセージが表示されます。

example.cpp:9:13: error: cannot cast 'B' to its private base class 'A'
    A* ab = new B;
            ^
example.cpp:6:11: note: declared private here
class B : private A { };
          ^~~~~~~~~
1 error generated.

私はC ++の専門家ではありませんが、単に許可されていないようです。スペックをざっと見て、思いついたものを見ていきます。

編集:これは仕様からの関連する参照です-セクション4.10ポインター変換、パラグラフ3:

タイプのprvalue「へのポインタCV D」、Dクラス型では、型のprvalue「CVへのポインタに変換することができるBBは、の基底クラスです」D。場合Bのアクセスできないまたは曖昧な基底クラスですD。この変換を必要とプログラムは、病気-形成されています。


5

それは非常に単純です。A私的に継承されるという事実は、B拡張するという事実がA秘密であり、それをB「知っている」だけであることを意味します。それが私的継承の定義そのものです。


4
しかし、に置き換えるprivateとまったく同じエラーが発生しprotectedます。
Lazer 2012年

2
確かに。「保護されている」とは、知識がのBサブクラス(および友人)に限定されていることを意味しBます。" A* ab = new B;"Cは、のサブクラスである架空のクラスでは合法ですB
アーネストフリードマン-ヒル

3

プライベート継承とは、派生クラスの外部では、継承情報が非表示になることを意味します。つまり、派生クラスを基本クラスにキャストすることはできません。関係は呼び出し元に認識されていません。


ありがとう、でもどういうわけか意味がありません。private■唯一のビジネスは、メンバーの行動を制御することです。継承情報が隠されていない場合、どのような害がありますか?
Lazer 2012年

1
プライベート継承は、集約/構成の形式です。それはする方法です持ってなくて、基本クラスのプロパティをされ、基本クラスのオブジェクト。それがあなたが望むものでないなら、私的な継承はあなたのためではありません。それがまさにその仕組みです。
tmpearce 2012年

0

これは機能しています

#include <iostream>

using namespace std;

class A{
    public:
        virtual void update() = 0;
};

class B: public A{
    public:
    virtual void update(){std::cout<<"hello";};
};

int main()
{
    A *a = new B();

    a->update();

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