15

私はこのコードを持っています(私の現実の問題から考案されました)

ExtendsB が実装されていないと不平を言って、コンパイルできませんB::Run(A* a)。ただし、拡張を理解するのに問題はありませんA* Run();

class A { };

class ExtendsA : public A { };

class B
{
public:
    virtual ~B(){}  
    virtual void Run(A* a) = 0;
    virtual A* Run() = 0;
};

class ExtendsB : public B
{
public:
    virtual ~ExtendsB(){}

    // Not OK! It does not see it as an implementation of 
    // virtual void Run(A* a) = 0;
    virtual void Run(ExtendsA* ea) {}; 
    virtual ExtendsA* Run() { return new ExtendsA(); }; // OK
};

C++ で戻り値の型をサブクラスに変更できるのに、パラメーターの型を変更できないのはなぜですか?

それは正当な理由ですか、それとも言語仕様の見落としですか?

4

4 に答える 4

17

C++ で戻り値の型をサブクラスに変更できるのに、パラメーターの型を変更できないのはなぜですか?

C++ 標準では、仮想関数をオーバーライドするときにCovariant の戻り値の型を使用できますが、関数のパラメーターを変更することはできません。もちろん、その背後には正当な理由があります。

根拠:

オーバーライドとは、基本的に、ポインタが指す実際のオブジェクトに応じて、実行時に Base クラス メソッドまたは Derived クラス メソッドのいずれかが呼び出されることを意味します。
つまり
「基本クラスのメソッドを呼び出すことができるすべてのインスタンスは、呼び出し元のコードを変更することなく、派生クラスのメソッドの呼び出しに置き換えることができます。」

上記のルールが適用されていない場合、新しい機能 (新しい派生クラス) を追加することで、既存のコードを壊す余地が残されます。

基本クラスの仮想関数 wrt パラメーターとは異なる関数プロトタイプが派生クラスにある場合、上記のルールが破られるため、関数は基本クラスの関数をオーバーライドしません。

ただし、共変の戻り値の型はこの規則に違反しません。これは、アップキャストが暗黙的に行われ、基底クラスのポインターが常にキャストなしで派生クラス オブジェクトを指すことができるためです。

于 2012-09-11T12:02:22.450 に答える
9

エラーは非常に明確です:void Run(A*)クラスExtendedBに必要ですが、それがありません。あなたが持っているvoid Run(ExtendedA*)のは だけですが、それは同じではありません: 基本クラスは任意の A-pointer を受け入れることを約束しますが、あなたExtendedB::Runはよりうるさく、狭いサブセットのみを受け入れます。

(共分散と反分散を混同していますが、C++ では反変のオーバーライドが許可されていないため、C++ には関係ありません。)

于 2012-09-11T12:01:19.363 に答える
4

これは単に 2 つの異なる型であるため、2 つの異なるシグネチャを持つ 2 つの異なる関数になります。

一般に、C++11 を理解するコンパイラを使用している場合は、別の関数をオーバーライドすることを意図した関数で override キーワードを使用する必要があります。あなたの場合、抽象基本クラスのためにエラーが明らかになりましたが、他の場合、そのようなエラーは多くのデバッグを引き起こす可能性があります...

于 2012-09-11T12:02:26.243 に答える
2

戻り値の型を特殊化すると、サブクラスがより厳密になりAます。Runただし、元の引数のサブクラスのみを受け入れるようにメソッドを制限Bすると、A.

継承は is-a 関係をモデル化します。あなたのコードはリスコフ置換の原則に違反しています。

于 2012-09-11T12:03:11.907 に答える