1

これは長い間C++ユーザーにとって簡単な質問だと確信しています。これはパターンであるか、問題を他の方法で解決する必要がありますが、私がPython開発者であり、C++の初心者であることを考えると、それがどのようになっているのかわかりません通常行われます。

インターフェイスを尊重する2つの異なるクラスのいずれかであるオブジェクトへのポインターを格納するクラスがあるとします。たとえば、次のようになります。

class AllPlayers
{
  public:
  virtual void play();
};

class VlcPlayer: public AllPlayers
{
  public:
  virtual void play();
};

class Mplayer: public AllPlayers
{
  public:
  virtual void play();
};

class MyMediaPlayer
{
  public:
  MyMediaPLayer(int playerType);
  AllPlayers m_player;
};

MyMediaPlayer::MyMediaPlayer(int PlayerType)
{
  if (PlayerType == 0) {
    VlcPlayer tmp_player;
    m_player = static_cast<AllPlayers> (tmp_player);
  }
  else {
    Mplayer tmp_player;
    m_player = static_cast<AllPlayers> (tmp_player);
  }
}

MyMediaPlayer test(0);
test.play();

まず、私はこれが機能しないことを知っています。なぜなら、どうすればこの効果を得ることができるのでしょうか? インターフェイスを使用して実装された同じメソッドを使用するためのクラスのメンバーが必要であり、いずれかを使用するたびにすべての派生クラスにキャストしようとするのを避けたい彼の方法。

4

3 に答える 3

2

C++ は値ベースです。つまり、特定の型のオブジェクトを作成すると、実際にはその型のオブジェクトになります。これは、動的ポリモーフィズムではうまく機能しません。動的ポリモーフィズムを取得するには、実際のオブジェクトへのポインターまたは参照を使用します。また、ライフタイムをまっすぐにするには、通常、対応するオブジェクトをスタックに割り当てます (ベースへのポインターを使用して派生型のオブジェクトを解放する場合は、ベース クラスに仮想デストラクタがあることを確認してください)。これで準備は完了です。ベースへのポインタを介してベース クラスの仮想関数を呼び出すだけです。派生クラスで関数をオーバーライドすると、この関数が呼び出されます。

于 2012-09-17T22:06:42.857 に答える
1

これが機能しない理由は、口語的にオブジェクト スプライシングとして知られています。(または、そこにいるハリー・ポッターの読者のために、オブジェクト・スプリンチ)

例を見てみましょう:

class Foo
{
public:
    int bob;
    float fred;

    // Foo(const Foo& otherfoo); // implicit copy constructor
};

class Bar : public Foo
{
public:
    double gabe; // gabe newell is fat
    char steve;  // steve jobs is thin

    // Bar(const Bar& otherbar); // implicit copy constructor
};

int main()
{
    Foo f;
    Bar b;

    f.bob = 10;
    f.fred = 1.5;

    b.bob = 15;
    b.fred = 2.5;
    b.gabe = 1.77245385091; // sqrt(pi)
    b.steve = -4;

    f = Foo(b);
    return 0;
}

これは合法かつ有効です。問題は、Foo の暗黙のコピー コンストラクターが呼び出され、Foo のコピー コンストラクターが Bar が何であるかを何も知らないことです。Foo が持っているすべてのものと、余分な無関係ながらくたが含まれているだけです。このため、Foo のデータのみが保持されます。バーに固有のデータが継ぎ合わされます。

これは定義された動作であることに注意することが重要です。それは、あなたが指示したことを正確に実行しています。基本クラスのサブクラスと基本クラスの間のキャストは暗黙的です。さらに、コピー コンストラクターの動作は暗黙的です。

内部的には、C++ ポインターと参照は同じように機能することに注意することも重要です。Bar を Foo のコピー コンストラクターに参照渡しするのはまったく問題ありません。この参照渡しでは、オブジェクトのコピーは生成されません。ポインターを操作するのと同じです。

実際のスプライシングは、コピー コンストラクターが噛み切れないほど噛み砕いた直接的な結果として発生します。予想よりも多くの状態を持つオブジェクトを取得し、唯一の選択肢は余分な状態を無視することです。

Python では、すべてが暗黙的に参照型として格納されるため、これは発生しません。参照のみを操作するため (オブジェクト自体は抽象化されています)、誤ってオブジェクトをスプライスする機会はありません。

于 2012-09-17T22:21:20.567 に答える
1

あなたが書くなら

AllPlayers m_player;

それはのインスタンスにAllPlayersなり、それから派生したクラスのインスタンスになることはできません。

代わりにポインターを使用して、クラスをスタックに割り当てる必要があります。

例: class MyMediaPlayer { public: MyMediaPLayer(int playerType); 〜MyMediaPlayer(); AllPlayers m_player; };

MyMediaPlayer::MyMediaPlayer(int PlayerType)
{
  if (PlayerType == 0) {
    m_player = new VlcPlayer;
  }
  else {
    m_player = new Mplayer;
  }
}

MyMediaPlayer::~MyMediaPlayer()
{
  if (0 != m_player) {
    delete m_player;
    m_player = 0;
  }
}

@xception で示唆されているように、を使用するとunique_ptr、インスタンスの割り当てを解除するコードを記述する必要がなくなります。

@DietmarKühl によって正しく指摘されているように、AllPlayers.

class AllPlayers
{
  public:
  virtual ~AllPlayers();
  virtual void play(); // note: this should probably be pure virtual.
};
于 2012-09-17T22:17:22.580 に答える