C++ 销毁时从单一托管容器中删除对象
我有一个C++ 销毁时从单一托管容器中删除对象,c++,singleton,shared-ptr,C++,Singleton,Shared Ptr,我有一个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); ret
Singleton
类,它管理Item
s的容器,公开允许从容器中添加或删除Item
s的公共函数
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;
};
当我运行下面的示例时,我在Singleton::Remove()
上遇到了一个崩溃,特别是mContainer.begin()
上的EXC\u BAD\u访问
这似乎表明mContainer
不再存在。查看调用堆栈,我还可以看到根调用堆栈框架之一是析构函数Singleton::~Singleton()
,这可以解释为什么mContainer
不再存在
我尝试了另一种方法:我没有使用std::shared_ptr
,而是使用原始指针(即Item*
),并在代码中进行适当的替换。它毫无问题地工作
我的问题是:
- 我猜现在发生的情况是,
项
对象的所有权只有在销毁了单例
后才由共享的ptr
释放,这导致了错误。这是正确的吗
- 如果
Singleton
中的容器是shared\u ptr
,是否不可能完成我想做的事情
- 如果没有,我怎么做
尽管这样做首先是明智的,但是如果您愿意使用并且遵守std::enabled\u shared\u from\u this
的限制,您想要的东西是可以实现的。见下文:
#include <iostream>
#include <algorithm>
#include <memory>
#include <vector>
struct Item;
typedef std::shared_ptr<Item> ItemPtr;
class Singleton
{
private:
Singleton() {}
public:
static Singleton &Instance()
{
static Singleton s;
return s;
}
void Add(ItemPtr item)
{
mContainer.emplace_back(std::move(item));
}
void Remove(const ItemPtr& item)
{
mContainer.erase(
std::remove(mContainer.begin(), mContainer.end(), item),
mContainer.end());
}
void Clear()
{
mContainer.clear();
}
private:
std::vector<ItemPtr> mContainer;
};
// note derivation. this means you can get a std::shared_ptr<Item>
// via `shared_from_this` , but it also means the object itself
// MUST be an actual shared object to begin with.
struct Item : public std::enable_shared_from_this<Item>
{
void Add()
{
Singleton::Instance().Add(shared_from_this());
}
};
int main()
{
ItemPtr a = std::make_shared<Item>();
ItemPtr b = std::make_shared<Item>();
// add to the singleton container
a->Add();
b->Add();
// report reference count of 'a'
std::cout << "before removal 'a' has " << a.use_count() << " references\n";
Singleton::Instance().Remove(a);
std::cout << "after removal 'a' has " << a.use_count() << " references\n";
}
其中最重要的部分是在main
中创建a
和b
。请注意,它们实际上是由std::shared\u ptr
从一开始就隐藏起来的。这是使该中的_共享_正常工作所必需的。其余的则相当直截了当。通过基类std::enable_shared_from_this
提供的shared_from_this()
成员,可以从项的任何成员体中获取引用bumpedstd::shared_ptr
简而言之,采用这种方法对您有效,但在任何情况下都不能使用shared\u from\u this()
,除非它所触发的对象首先已经由std::shared\u ptr
管理。记住这一点。在顶部,Singleton::Remove
表现出未定义的行为mContainer.erase(它)
使it
无效,然后it++
访问这个现在无效的迭代器。您可能正在查找ItemPtr(this)
是个坏主意,因为它会导致双重破坏:ItemPtr
temporary在分号处被破坏,并将在this
上调用delete
。原始指针a
和b
隐藏在推到容器中的std::shared\u ptr
实例中。那些赤裸裸的delete
调用是错误的。动态对象要么由智能指针管理,要么不是。你不可能两全其美,当然你也不可能让他们最终毁灭两次。您绝对应该从_this
中查看启用的_shared _,并记住,如果您选择该路径,则任何生成std::shared _ptr
的实例请求都必须从初始创建时就进行管理。由于该对象在单例中始终处于活动状态,因此该对象将如何被销毁?这有点循环。
int main()
{
Item* a = new Item();
Item* b = new Item();
a->Add();
b->Add();
delete a;
delete b;
}
#include <iostream>
#include <algorithm>
#include <memory>
#include <vector>
struct Item;
typedef std::shared_ptr<Item> ItemPtr;
class Singleton
{
private:
Singleton() {}
public:
static Singleton &Instance()
{
static Singleton s;
return s;
}
void Add(ItemPtr item)
{
mContainer.emplace_back(std::move(item));
}
void Remove(const ItemPtr& item)
{
mContainer.erase(
std::remove(mContainer.begin(), mContainer.end(), item),
mContainer.end());
}
void Clear()
{
mContainer.clear();
}
private:
std::vector<ItemPtr> mContainer;
};
// note derivation. this means you can get a std::shared_ptr<Item>
// via `shared_from_this` , but it also means the object itself
// MUST be an actual shared object to begin with.
struct Item : public std::enable_shared_from_this<Item>
{
void Add()
{
Singleton::Instance().Add(shared_from_this());
}
};
int main()
{
ItemPtr a = std::make_shared<Item>();
ItemPtr b = std::make_shared<Item>();
// add to the singleton container
a->Add();
b->Add();
// report reference count of 'a'
std::cout << "before removal 'a' has " << a.use_count() << " references\n";
Singleton::Instance().Remove(a);
std::cout << "after removal 'a' has " << a.use_count() << " references\n";
}
before removal 'a' has 2 references
after removal 'a' has 1 references