在销毁时从单例管理的容器中删除对象

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_ACCESSmContainer.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*)并在代码中进行了适当的替换。它工作没有问题。

我的问题是:

尽管这样做的初衷是明智的,但只要您愿意使用并遵守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 中创建 ab。请注意,实际上,它们从一开始就由 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 管理.请记住这一点。