7

C++ では、純粋な virtualメソッドが強制的なオーバーライドを直接の子 (オブジェクト作成用)のみに強制し、孫などには強制しないのはなぜですか?

struct B {
  virtual void foo () = 0;
};
struct D : B {
  virtual void foo () { ... };
};

struct DD : D {
  // ok! ... if 'B::foo' is not overridden; it will use 'D::foo' implicitly
};

この機能を除外しても大したことはないと思います。
たとえば、言語設計の観点から、 のような明示的なステートメントがある場合にのみstruct DD使用が許可される可能性がありました。それ以外の場合は、強制をオーバーライドする必要があります。D::foousing D::foo;foo

C++ でこの効果を得る実用的な方法はありますか?

4

5 に答える 5

8

少なくとも、オーバーライドされたメソッドを明示的にアナウンスするように求められるメカニズムが 1 つ見つかりました。しかし、それは完璧な方法ではありません。

base にいくつかvirtualの純粋なメソッドがあるとしますclass B

class B {
  virtual void foo () = 0;
  virtual void bar (int) = 0;
};

foo()その中で、階層全体によってのみオーバーライドされたいとします。簡単にするために、virtualその特定のメソッドを含む基本クラスが必要です。そのメソッドと同じ型を受け入れるだけのテンプレートコンストラクターがあります。

class Register_foo {
  virtual void foo () = 0; // declare here
  template<typename T>  // this matches the signature of 'foo'
  Register_foo (void (T::*)()) {}
};
class B : public virtual Register_foo {  // <---- virtual inheritance
  virtual void bar (int) = 0;
  Base () : Register_foo(&Base::foo) {}  // <--- explicitly pass the function name
};

階層内の後続のすべての子クラスは、そのすべてのコンストラクター内に明示的に登録する必要があります。例えば:foo

struct D : B {
  D () : Register_foo(&D::foo) {}
  virtual void foo () {};
};

この登録メカニズムは、ビジネス ロジックとは関係ありません。ただし、子classは自分自身fooまたは親のfoo、または同様の構文メソッドを使用して登録することを選択できますが、少なくともそれは明示的に発表されます。

于 2012-03-12T04:00:35.970 に答える
2

あなたの例では、D::foopure を宣言していません。そのため、オーバーライドする必要はありません。再度オーバーライドする必要がある場合は、pure として宣言します。

をインスタンス化できるようにしたいがD、それ以降の派生クラスにオーバーライドを強制する場合fooは、できません。Dただし、 pure を再宣言したクラスからさらに別のクラスを派生させることもできます。そのクラスから派生したクラスは、それを再度オーバーライドする必要があります。

于 2012-02-28T07:07:33.360 に答える
2

基本的に求めているのは、最も派生したクラスが関数を実装することを要求することです。そして私の質問は:なぜですか?これが関連していると想像できるのは、同じ型の新しいインスタンスを返すclone()or のような関数だけです。another()そして、それが本当に強制したいことであり、新しいインスタンスが同じ型を持つことです。そこであっても、機能が実際に実装されている場所は関係ありません。そして、あなたはそれを強制することができます:

class Base
{
    virtual Base* doClone() const = 0;
public:
    Base* clone() const
    {
        Base* results = doClone();
        assert( typeid(*results) == typeid(*this) );
        return results;
    }
}

(実際には、オーバーライドを忘れてclone実際の問題になる人を見つけたことがないので、上記のようなことを気にしたことはありません。ただし、事後条件を強制したいときはいつでも、これは一般的に役立つ手法です。)

于 2012-02-28T08:52:32.900 に答える
1

純粋仮想とは、インスタンス化するために、純粋仮想関数を宣言するクラスの子孫で純粋仮想をオーバーライドする必要があることを意味します。これは、インスタンス化されるクラス、または純粋な仮想を宣言するベースとインスタンス化されるベースの間の任意の中間クラスにある可能性があります。

ただし、純粋な仮想をオーバーライドせずに、純粋な仮想を持つクラスから派生する中間クラスを持つことは引き続き可能です。純粋な仮想を宣言するクラスと同様に、これらのクラスはベースクラスとしてのみ使用できます。これらのクラスのインスタンスを作成することはできません。それらから派生したクラスのみを作成し、すべての純粋な仮想が実装されています。

子孫が仮想をオーバーライドすることを要求する限り、中間クラスがすでにそうしているとしても、答えはノーです。C++は少なくともそれを行うことを意図したものを何も提供しません。複数の(おそらく仮想の)継承を使用して何かを一緒にハックできる可能性があるように思われるので、中間クラスの実装は存在しますが、それを使用しようとするとあいまいになりますが、それを十分に理解しているとは思いませんでしたどのように(またはもし)それが機能するか-そしてそれが機能したとしても、オブジェクトをインスタンス化するだけでなく、問題の関数を呼び出そうとしたときにのみそのトリックを実行します。

于 2012-02-28T07:18:50.683 に答える
1

C++ でこの効果を得る実用的な方法はありますか?

いいえ、正当な理由があります。これが標準の一部である場合、大規模なプロジェクトでのメンテナンスを想像してみてください。一部の基本クラスまたは中間基本クラスは、抽象インターフェイスであるパブリック インターフェイスを追加する必要があります。さて、そのすべての子と孫を変更して再コンパイルする必要があります(あなたが提案したように D::foo() を使用して追加するのと同じくらい簡単だったとしても)、おそらくこれがどこに向かっているのかがわかります。

本当に実装を強制したい場合は、子クラスで他の純粋仮想の実装を強制できます。これは、CRTP パターンを使用して行うこともできます。

于 2012-03-09T21:01:50.310 に答える