在销毁时从单例管理的容器中删除对象
Removing an object from a singleton-managed container upon destruction
我有一个 Singleton
class 来管理 Item
的容器,公开 public 允许添加或删除 Item
的函数来自容器。
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()
上崩溃,特别是 EXC_BAD_ACCESS
在 mContainer.begin()
上崩溃。
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>
是不是就不能做我想做的事情了?
- 如果没有,我该怎么做?
尽管这样做的初衷是明智的,但只要您愿意使用并遵守std::enabled_shared_from_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";
}
输出
before removal 'a' has 2 references
after removal 'a' has 1 references
其中最重要的部分是在 main
中创建 a
和 b
。请注意,实际上,它们从一开始就由 std::shared_ptr
管理。这是 std::enable_shared_from_this
正常工作所必需的。剩下的就相当简单了。在 Item
的任何成员的 体内从 中获取引用碰撞 std::shared_ptr
的能力是通过基础提供的 shared_from_this()
成员完成的class std::enable_shared_from_this
.
简而言之,采用这种方法对你有用,但你不能在任何时候使用 shared_from_this()
除非它被触发的对象首先已经由 std::shared_ptr
管理.请记住这一点。
我有一个 Singleton
class 来管理 Item
的容器,公开 public 允许添加或删除 Item
的函数来自容器。
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()
上崩溃,特别是 EXC_BAD_ACCESS
在 mContainer.begin()
上崩溃。
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>
是不是就不能做我想做的事情了? - 如果没有,我该怎么做?
尽管这样做的初衷是明智的,但只要您愿意使用并遵守std::enabled_shared_from_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";
}
输出
before removal 'a' has 2 references
after removal 'a' has 1 references
其中最重要的部分是在 main
中创建 a
和 b
。请注意,实际上,它们从一开始就由 std::shared_ptr
管理。这是 std::enable_shared_from_this
正常工作所必需的。剩下的就相当简单了。在 Item
的任何成员的 体内从 中获取引用碰撞 std::shared_ptr
的能力是通过基础提供的 shared_from_this()
成员完成的class std::enable_shared_from_this
.
简而言之,采用这种方法对你有用,但你不能在任何时候使用 shared_from_this()
除非它被触发的对象首先已经由 std::shared_ptr
管理.请记住这一点。