7

共通コンテナ内の共有インターフェースクラスから派生したクラスのオブジェクトの束を管理したいと思います。

問題を説明するために、さまざまなアクターを含むゲームを作成しているとしましょう。IActorインターフェイスを呼び出して、それから派生EnemyしてみましょうCivilian

さて、アイデアは私のゲームのメインループがこれを実行できるようにすることです:

// somewhere during init
std::vector<IActor> ActorList;
Enemy EvilGuy; 
Civilian CoolGuy;
ActorList.push_back(EvilGuy);
ActorList.push_back(CoolGuy);

// main loop
while(!done) {
    BOOST_FOREACH(IActor CurrentActor, ActorList) {
        CurrentActor.Update();
        CurrentActor.Draw();
    }
}

...またはそれらの線に沿った何か。この例は明らかに機能しませんが、それが私がここで尋ねている理由です。

知りたいのですが、一般的な異種コンテナ内のこれらのオブジェクトを管理するための最良、最も安全、最高レベルの方法は何でしょうか?さまざまなアプローチ(Boost :: Any、void *、boost :: shared_ptr、Boost.Pointer Container、dynamic_castを使用したハンドラークラス)については知っていますが、どちらを使用するかを決めることはできません。

また、手動のメモリ管理やネストされたポインタからできるだけ離れたいことを強調したいと思います。

感謝します:)。

4

4 に答える 4

10

あなたが言及した問題を解決するために、あなたは正しい方向に進んでいますが、あなたはそれを間違った方法でやっています。これはあなたがする必要があることです

  • Enemy派生クラスによってオーバーライドされる仮想関数を使用して(すでに実行している)基本クラスを定義Civilianします。
  • オブジェクトを保存する適切なコンテナを選択する必要があります。あなたはstd::vector<IActor>良い選択ではないので 取った
    • まず、ベクトルにオブジェクトを追加すると、オブジェクトのスライスにつながります。これは、オブジェクト全体ではなく、またはのIActor一部のみが保存されていることを意味します。EnemyCivilian
    • 次に、オブジェクトのタイプ()に応じて関数を呼び出す必要がありますvirtual functions。これは、ポインターを使用する場合にのみ発生する可能性があります。

上記の両方の理由は、のようなポインタを含むことができるコンテナを使用する必要があるという事実を示していますstd::vector<IActor*>。しかし、より良い選択はcontainer of smart pointers、メモリ管理の頭痛の種からあなたを救うことになる使用することです。必要に応じて、任意のスマートポインタを使用できます(ただし、使用できませんauto_ptr) 。

これはあなたのコードがどのように見えるかです

// somewhere during init
std::vector<some_smart_ptr<IActor> > ActorList;
ActorList.push_back(some_smart_ptr(new Enemy()));
ActorList.push_back(some_smart_ptr(new Civilian()));

// main loop
while(!done) 
{
    BOOST_FOREACH(some_smart_ptr<IActor> CurrentActor, ActorList) 
    {
        CurrentActor->Update();
        CurrentActor->Draw();
     }
}

スマートポインタの部分を除いて、元のコードとほとんど同じです。

于 2010-03-22T02:39:04.303 に答える
4

dynamic_cast私の即座の反応は、スマートポインターをコンテナーに格納し、基本クラスが派生クラスに戻る必要がない十分な(純粋な)仮想メソッドを定義していることを確認する必要があるということです。

于 2010-03-22T01:41:10.197 に答える
3

コンテナーがその中の要素を排他的に所有するようにしたい場合は、Boostポインターコンテナーを使用します。それらはそのジョブ用に設計されています。それ以外の場合は、のコンテナをshared_ptr<IActor>使用します(もちろん、適切に使用します。つまり、所有権を共有する必要があるすべての人が使用しますshared_ptr)。

IActorどちらの場合も、のデストラクタが仮想であることを確認してください。

void*手動でメモリ管理を行う必要があるので、それで終わりです。Boost.Anyは、型が継承によって関連付けられている場合はやり過ぎです-標準的なポリモーフィズムがその役割を果たします。

必要かどうかdynamic_castは直交する問題です。コンテナのユーザーがIActorインターフェイスのみを必要とし、(a)インターフェイスのすべての機能を仮想化するか、(b)非仮想インターフェイスイディオムを使用する場合、その場合、dynamic_castは必要ありません。コンテナのユーザーが、一部のIActorオブジェクトが「実際に」民間人であることを知っていて、IActorではなくCivilianインターフェイスにあるものを利用したい場合は、キャスト(または再設計)が必要になります。

于 2010-03-22T11:56:28.483 に答える
3

ご想像のとおり、オブジェクトをポインタとして保存する必要があります。
私は(スマートポインターの通常のコンテナーではなく)ブーストポインターコンテナーを使用することを好みます。

この理由は、ブーストptrコンテナが、ポインタではなくオブジェクト(参照を返す)であるかのようにオブジェクトにアクセスするためです。これにより、コンテナで標準のファンクタとアルゴリズムを簡単に使用できるようになります。

スマートポインタの欠点は、所有権を共有していることです。
これはあなたが本当に望んでいることではありません。所有権を単一の場所(この場合はコンテナー)に配置する必要があります。

boost::ptr_vector<IActor> ActorList; 
ActorList.push_back(new Enemy()); 
ActorList.push_back(new Civilian());

std::for_each(ActorList.begin(), 
              ActorList.end(),
              std::mem_fun_ref(&IActor::updateDraw));
于 2010-03-22T14:27:08.423 に答える