0

これはばかげた質問かもしれません。ここで壁にぶつかっているように見えるので、私は答えを知っていると思います (いいえ)。

特定のクラスから派生したオブジェクトのコレクションがあるとします。

class BaseClass;
class DerivedA: public BaseClass;
class DerivedB: public BaseClass;
class DerivedC: public BaseClass;
std::vector<BaseClass> myCollection;

特定のクラスの型に応じてメソッドを呼び出したい:

class Processor {
  void doSomething(DerivedA a, DerivedB b);
  void doSomething(DerivedA a, DerivedC c);
}

問題は、コレクションの個々のアイテムにアクセスし、「プロセッサ」で「doSomething」メソッドを呼び出そうとすると、使用するメソッドを決定できないことです (afaik)。だから私の質問は次のとおりです:コレクション内のアイテムを正しい派生型で取得する方法はありますか?

4

2 に答える 2

2

メソッドをそのままにしておく場合、これは多重ディスパッチdoSomethingと呼ばれるものであり、現在 C++ ではサポートされていません。

それが仮想メンバー関数であるBaseClass場合、はい、それが呼び出されているオブジェクトでのミル C++ ポリモーフィズムの実行になりますが、それでも引数の型を自動的に推測しません。

これを回避するには、前のリンクで提案されているようなことを行うことができます

void collideWith(Thing& other) {
     // dynamic_cast to a pointer type returns NULL if the cast fails
     // (dynamic_cast to a reference type would throw an exception on failure)
     if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
         // handle Asteroid-Asteroid collision
     } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
         // handle Asteroid-Spaceship collision
     } else {
         // default collision handling here
     }
 }

基本的に、1 つが機能するまでさまざまな派生クラスにキャストし続け、メソッドの 1 つを適切に呼び出します (コンパイラーはキャストしようとしている型を認識しているため、特別な作業は必要ありません)。

重要: @WhozCraig が指摘するように、オブジェクトのスライスを回避し、この質問全体を意味のないものにするために、ベクトルはポインターを保持する必要があります。

于 2012-12-27T15:37:12.310 に答える
1

はい、上記のようにポリモーフィズムを使用する必要があります。非常に複雑になりますが、関数が2つのオブジェクトを処理する必要がある場合。

派生が限定されたセットを形成し、お互いを知っている場合は、二重ディスパッチを使用できます。完璧ではありませんが、この特定のケースを解決します。

class DerivedA;
class DerivedB;
class DerivedC;

class BaseClass
{
 public:
     virtual ~BaseClass();

     virtual void doSomethingWithBase( BaseClass & b2 ) = 0;
     virtual void doSomethingWithDerivedA( DerivedA & da ) = 0;
     virtual void doSomethingWithDerivedB( DerivedB & db ) = 0;
     virtual void doSomethingWithDerivedC( DerivedC & dc ) = 0;
};

class DerivedA : public BaseClass
{
   public:

      void doSomethingWithBase( BaseClass & b2 )
      {
           b2.doSomethingWithDerivedA( *this );
      }

      void doSomethingWithDerivedA( DerivedA & da )
      {
           // implement for two DerivedA objects
      }

      void doSomethingWithDerivedB( DerivedB & db )
      {
           // implement for an A and B
      }

      void doSomethingWithDerivedC( DerivedC & dc )
      {
          // implement for an A and C
      }
 };

 // implement DerivedB to call doSomethingWithDerivedB on its parameter
 // implement DerivedC to call doSomethingWithDerivedC on its parameter.

あなたはアイデアを得る。呼び出し元から、どの 2 つのタイプがあるかを知る必要はなく、実際にこれを調べる必要もありません。しかし、実装をさらに追加する場合は、編集するコードが多くなり、ある種のルックアップ テーブルを検討する必要があります。

それ自体を定義するクラスが必要な場合は、ある種の仮想 ID を使用できます。

  class BaseClass
  {
      public:
         virtual int id() const = 0;
  };

次に、クラスを取得して ID を明らかにし、これらの ID に基づいて、2 つのオブジェクトを処理するハンドラーをテーブルで見つけます。ID は int である必要はありません。名前の衝突を回避しやすくする文字列にすることができます。これは、派生クラスを知らない、またはお互いを知っている基本クラスの二重ディスパッチ メソッドよりも利点があります。拡張可能であること。また、すべてのペアを処理する必要はありません。

于 2012-12-27T15:34:39.723 に答える