【发布时间】:2021-03-06 19:06:20
【问题描述】:
我有一个Singleton 类,它管理Items 的容器,公开允许在容器中添加或删除Items 的公共函数。
class Item;
typedef std::shared_ptr<Item> ItemPtr;
class Singleton
{
public:
static Singleton& Instance()
{
static std::unique_ptr<Singleton> Instance(new Singleton);
return *Instance;
}
void Add(ItemPtr item)
{
mContainer.push_back(item);
}
void Remove(ItemPtr item)
{
for (auto it = mContainer.begin(); it != mContainer.end(); it++)
if (*it == item)
mContainer.erase(it);
}
private:
std::vector<ItemPtr> mContainer;
};
我希望Item 能够通过Add() 方法将自己添加到Singleton 容器中,并在容器销毁时将其从容器中移除。
class Item
{
public:
Item() {}
~Item()
{
Singleton::Instance().Remove(ItemPtr(this));
}
void Add()
{
Singleton::Instance().Add(ItemPtr(this));
}
};
当我运行下面的示例时,我在Singleton::Remove() 上遇到了崩溃,特别是在mContainer.begin() 上出现了EXC_BAD_ACCESS。
int main()
{
Item* a = new Item();
Item* b = new Item();
a->Add();
b->Add();
delete a;
delete b;
}
这似乎表明mContainer 不再存在。查看调用堆栈,我还可以看到根调用堆栈帧之一是析构函数Singleton::~Singleton(),这可以解释为什么mContainer 不再存在。
我尝试了一种不同的方法:我没有使用std::shared_ptr<Item>,而是简单地使用了原始指针(即Item*),并在代码中进行了适当的替换。它没有问题。
我的问题是:
- 我猜发生的事情是
Item对象的所有权只有在Singleton被销毁后才被shared_ptr释放,这会导致错误。这是正确的吗? - 如果
Singleton中的容器是shared_ptr<Item>,是不是就不能做我想做的事了? - 如果没有,我该怎么办?
【问题讨论】:
-
最重要的是,
Singleton::Remove表现出未定义的行为。mContainer.erase(it)使it无效,然后it++访问这个现在无效的迭代器。 -
您可能正在寻找
std::enable_shared_from_this。ItemPtr(this)是个坏主意,因为它会导致双重破坏:ItemPtr临时在分号处被破坏,并将在this上调用delete。 -
原始指针
a和b隐藏在推入容器的std::shared_ptr实例中。那些赤裸裸的delete电话是错误的。动态对象要么由智能指针管理,要么不是。你不能两全其美,而且你当然不能让他们的最终毁灭两次。你绝对应该查看enabled_shared_from_this,并记住,如果你走这条路,任何产生std::shared_ptr的实例请求必须从一开始就从一开始就进行管理。跨度> -
由于对象在单例中总是存在的,如何销毁该对象?这有点循环。
标签: c++ singleton shared-ptr