web-dev-qa-db-ja.com

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

$ 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++でこのタイプのコードを許可することの何が問題になっていますか?完全に無害に見えます。
77
Lazer

継承をプライベートにすることで、基本的に、BがAから継承するという事実でさえ、プライベートであり、外部からはアクセスできない/見えないということです。

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

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

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

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

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

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

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

94
Jerry Coffin

プライベート継承は、クラスBのメンバーが外界に見える方法のみを変更する必要があります

します。で、もし

A* p = new B;

許可された場合、Bの継承されたメンバーは、A*。それらは個人的に継承されているため、そのアクセスは違法であり、アップキャストも違法です。

11
Ben Voigt

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:

タイプが「cvD」へのポインター(Dはクラスタイプ)のprvalueは、prvalueに変換できます。タイプが「cv Bへのポインター」で、BはDの基本クラスです。 BDのアクセス不能または曖昧な基本クラスである場合、この変換を必要とするプログラムは不正な形式です。

7
Carl Norum

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

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

3
tmpearce