智能指针列表-管理对象生存期和指针有效性
List of smart pointers - Managing object lifetime and pointer validity
我有一个智能指针列表,其中每个指针指向一个单独的Entity类。
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> &&'
将每个指针分配到列表的最佳方法是什么?基于构造函数的版本是否可行?
我目前认为,应该处理每个Entity类的生存期的是智能指针列表,在构造函数中分配指针不是一个好的设计选择。在这种情况下,我可能应该创建一个CreateEntity方法,将指针添加到列表,而不是让构造函数处理它。这样更好吗?
在阅读了这里、这里和这里(场外)的问题后,我考虑了什么类型的智能指针适合此操作。根据我目前所读到的内容,很难得到确切的答案,尽管它们都提供了一些相互矛盾的建议。
以这种方式使用构造函数肯定不是一个好主意,因为构造函数没有关于如何创建和控制对象的信息——在堆栈上,静态地,通过一些智能指针动态地,通过哑指针动态地?
为了解决这个问题,您可以使用静态工厂方法来创建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指针容器库。它包含专门拥有的指针容器,如ptr_vector
、ptr_list
、ptr_map
等。
我不会对您的设计问题发表评论,但为了修复您的错误,请将您的代码更改为:
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<>
是一个仅移动类型(即没有复制构造函数)。
代码中的问题是您试图将std::unique_ptr<T>
从l值中移动。std::unique_ptr<T>
的实例化是不可复制的,并且只能移动。要从l值移动,您需要明确地这样做:
this->m_entities.push_back(std::move(b));
对std::move()
的调用不会真正移动任何东西,但它确实产生了一个类型,该类型向编译器指示对象可以移动。
要解决堆栈创建实例的问题,只需向构造函数添加一个参数,告诉它不要将新实例添加到列表中,例如:
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
破坏之间的冲突,这可能会变得有点复杂。
- GCC对可能有效的代码抛出init list生存期警告
- 在不复制临时对象的情况下延长其生存期
- 结束另一个线程中使用的对象的生存期
- "this"指针的值在对象的生存期内是否恒定?
- 创建具有全局生存期的 UObject
- C++17 和静态临时生存期的参考扩展
- 数组对象的生存期是否在重用其元素存储时结束?
- 共享指针生存期
- 具有空洞初始化的对象的生存期
- 如何在向量列表初始化时避免对象复制以及如何延长临时的生存期
- 指针引用的生存期(以 C++为单位)
- 指向对象生存期之外的已分配内存的指针是"invalid pointer[s]"还是"pointer[s] to an object"?
- 理解C++指针生存期/僵尸指针
- 共享指针和原始指针生存期
- 对临时与指向临时及其生存期的指针的引用
- 未'new'分配给指针而创建的对象的生存期
- 智能指针列表-管理对象生存期和指针有效性
- 指针是否延长了自动存储变量的生存期
- 并发对象池,其产品的生存期完全由包含对象的智能指针控制
- Lua用户数据指针的生存期