2

各ポインターが個別のエンティティ クラスを指すスマート ポインターのリストがあります。

std::list<std::unique_ptr<Entity>> m_entities;

クラスのインスタンス化時にコードによって「自動的に」処理されるため、コンストラクターが std::list クラスへの各ポインターの割り当てを処理するようにしたいと思います。ただし、この設計が悪い場合は、C# のバックグラウンドを持つ私にとってのみ意味があるため、より良い代替案を歓迎します。

Entity::Entity(Game &game) 
    : m_game(game),             
    m_id(m_game.g_idGenerator->generateNewID())             
{
    m_game.m_entities.push_back(std::unique_ptr<Entity>(this));
}

このメソッドで私が遭遇した主な問題は、Entity クラスの有効期間が Entity クラスによって管理されていないことです。

たとえば、Entity クラスをスタックに割り当てると、割り当てられたメソッドを離れた後に Entity デストラクタが呼び出され、ポインタが無効になります。

そこで、代わりにスマート ポインターを作成し、Entity クラスをヒープに割り当ててから、明示的にリストにポインターを追加する方法を検討しました。

std::unique_ptr<Entity> b(new Entity(*this));
m_entities.push_back(b);   // ERROR

これにより、次のエラーが発生します

error C2664: 'void std::list<_Ty>::push_back(_Ty &&)' : cannot convert parameter 1 from 'std::unique_ptr<_Ty>' to 'std::unique_ptr<_Ty> &&'

各ポインターをリストに割り当てるための最良のアプローチと考えられるものは何ですか?コンストラクターベースのバージョンは可能ですか?

私は現在、各エンティティ クラスの有効期間を処理する必要があるのはスマート ポインターのリストであり、コンストラクターでポインターを割り当てることは適切な設計上の選択ではないと考えています。その場合、コンストラクターに処理させるのではなく、ポインターをリストに追加する CreateEntity メソッドを作成する必要があります。これは良いですか?

ここここ、およびここ(オフサイト)にある質問を読んだ後、この操作に適したスマート ポインターのタイプを検討しました。私がこれまでに読んだ内容に基づいて正確な答えを得ることは困難ですが、それらはすべて多少矛盾するアドバイスを提供します.

4

4 に答える 4

4

コンストラクターをこのように使用することは、絶対に良い考えではありません。コンストラクターは、スタック上で、静的に、スマートポインターによって動的に、ダムポインターによって動的に、オブジェクトがどのように作成および制御されるかについての情報を持っていないからです。

この問題を解決するには、静的ファクトリ メソッドを使用してEntityインスタンスを作成します。

class Entity
{
public:

   // Variant with unique ownership
   static void CreateGameEntity(Game& game)
   {
     std::unique_ptr<Entity> p(new Entity());
     game.m_entities.push_back(std::move(p));
   }

   // OR (you cannot use both)

   // Variant with shared ownership
   static std::shared_ptr<Entity> CreateGameEntity(Game& game)
   {
     std::shared_ptr<Entity> p(new Entity());
     game.m_entities.push_back(p);
     return p;
   }

private:

   // Declare ctors private to avoid possibility to create Entity instances
   // without CreateGameEntity() method, e.g. on stack.
   Entity();
   Entity(const Entity&);
};    

どのスマート ポインターを使用しますか? まあ、これはあなたのデザイン次第です。オブジェクトがインスタンスGameのみを所有しEntity、その有効期間を完全に管理する場合、使用std::unique_ptrは問題ありません。ある種の共有所有権が必要な場合 (たとえば、Game同じEntityオブジェクトを共有できる複数のオブジェクトがある場合)、 を使用する必要がありますstd::shared_ptr

また、独自の所有権の場合は、Boost Pointer Container ライブラリを使用できます。ptr_vector、などptr_listの特殊な所有ポインター コンテナーが含まれています。ptr_map

于 2012-09-20T23:09:11.107 に答える
2

設計に関する質問にはコメントしませんが、エラーを修正するには、コードを次のいずれかに変更します。

m_entities.push_back(std::unique_ptr<Boundary>(new Boundary(*this, body)));

また:

std::unique_ptr<Boundary> b(new Boundary(*this, body));
m_entities.push_back(std::move(b));

その理由はb、コードでは左辺値ですstd::unique_ptr<>が、移動のみの型 (つまり、コピー コンストラクターがない) であるためです。

于 2012-09-20T22:39:32.397 に答える
2

std::unique_ptr<T>コードの問題は、左辺値からa を移動しようとすることです。のインスタンス化はstd::unique_ptr<T>コピー不可で、移動のみ可能です。左辺値から移動するには、明示的に移動する必要があります。

this->m_entities.push_back(std::move(b));

への呼び出しstd::move()は実際には何も移動しませんが、オブジェクトを移動できることをコンパイラに示す型を生成します。

于 2012-09-20T22:42:13.417 に答える
0

スタックで作成されたインスタンスの問題に対処するには、コンストラクターにパラメーターを追加して、新しいインスタンスをリストに追加しないように指示するだけです。次に例を示します。

Entity::Entity(Game &game, bool AddToList = true)  
    : m_game(game),              
      m_id(m_game.g_idGenerator->generateNewID())              
{ 
    if (AddToList) m_game.m_entities.push_back(this); 
} 

.

{
    ...
    Entity e(game, false);
    ...
}

Entity別のオプションは、エンティティがまだ存在する場合にリストから削除するデストラクタをエンティティに追加することですが、直接の破棄と破棄の間の競合を回避しようとすると、少し複雑になる可能性がありますunique_ptr

于 2012-09-20T22:41:42.767 に答える