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 类管理。

例如,如果我在堆栈上分配一个实体类,它将在离开分配它的方法后调用实体析构函数,并且指针将不再有效。

因此,我考虑了创建智能指针的替代方法,将实体类分配给堆,然后显式地将指针添加到列表中。

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 library。它包含专门的拥有指针容器,如,ptr_vector等。ptr_listptr_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 添加一个析构函数,如果它仍然存在,则将其从列表中删除,但这可能会变得有点复杂,以避免直接Entity破坏和unique_ptr破坏之间的冲突。

于 2012-09-20T22:41:42.767 回答