2

次の問題があるとします。

class Instrument {
};

class Guitar : public Instrument {
  public:
    void doGuitar() const;
};

class Piano : public Instrument {
  public:
    void doPiano() const;
};

へのポインタのリストを取得しましたInstrument

list<shared_ptr<Instrument>> instruments;

(たとえば)経由で楽器を追加する

Guitar myGuitar;
instruments.push_back(make_shared<Guitar>(myGuitar));

ここで、リストを反復処理して、現在の楽器がピアノで、ギターである場合にinstruments呼び出します。これら 2 つの関数は大きく異なるため、 class で抽象化することはできません。doPiano()doGuitar()Instrument

問題は、C++ が Instrument実行時に型を識別できないことですよね (単一ディスパッチのため)。イテレータが指している現在のタイプに応じて、ピアノまたはギター関数を呼び出すようにするにはどうすればよいですか。

sthを実装できたら嬉しいです。この疑似コードのように動作します:

list<shared_ptr<Instrument>>::const_iterator it;
if ("current type == Guitar")
  (*it)->doGuitar();
else if ("current type == Piano")
  (*it)->doPiano();

結果

実際、私は自分のアプローチでいくつかの問題に遭遇しました。この投稿を使用して多くのリファクタリングを行いました: How does one downcast a std::shared_ptr? . 助けてくれてありがとう:)

4

4 に答える 4

3

設計を改善してこの問題を解消できる可能性がありますが、既存の設計内で作業することで、多態的な引数として を受け取る仮想メンバー関数Instrument::play_itを追加できます。Playerには、(ギター引数を取る) と(ピアノ引数を取る)Playerの 2 つの関数があります。ギター クラスでは、self を引数として呼び出すようにオーバーライドします。ピアノクラスのオーバーライドでは、self を引数として呼び出します。キャストを見てください。play_guitarplay_pianoplay_itPlayer::play_guitarplay_itPlayer::play_piano

これは厳密には複数ディスパッチではなく、訪問者パターンとして知られています。visitorただし、物事に名前を付けたり、そのような説明のない愚かなことを始めたりしないように、あまり焦点を当てないのがおそらく最善です。

于 2016-12-12T09:37:39.950 に答える
0

ダブルディスパッチは次のように機能します (疑似コード、重要だが些細なことは省略):

struct InstrumentVisitor{
   // knows all instruments
   virtual void doGuitar(Guitar*) = 0;
   virtual void doPiano(Piano*) = 0;
};

class Instrument {
   virtual void doInstrument(InstrumentVisitor*) = 0;
   ...
 };

class Piano : public Instrument {
    void doInstrument (InstrumentVisitor* v) {
       v->doPiano(this);
};

class Guitar : public Instrument {
    void doInstrument (InstrumentVisitor* v) {
       v->doGuitar(this);
};

これで、具体的な訪問者を考案できます。

struct Player : InstrumentVisitor {
  // does vastly different things for guitar and piano
   void doGuitar (Guitar* g) {
      g->Strum(pick, A6);
   }
   void doPiano (Piano* p) {
      p->Scale (Am, ascending);
};
于 2016-12-12T09:57:54.727 に答える
0

タイプ消去は別のオプションです。

std::vector<std::function<void()>> playInstrument;
playInstrument.emplace_back([g = Guitar{}]() { return g.doGuitar(); });
playInstrument.emplace_back([p = Piano{} ]() { return p.doPiano();  });

playInstrument[0]();

このために、共通の基本クラスは必要ありません。

于 2016-12-12T11:44:56.353 に答える