std::shared_ptr 自定义参考计数器

std::shared_ptr custom reference counter

我目前正在编写一个代码,我需要在代码的多个部分之间静态共享一个对象的实例。所以基本上是一个单身人士。该实例将被共享,直到没有人再使用它为止。此时我需要在删除实际实例之前执行一些清理操作。

如果在此之后有人请求共享实例,它将获得一个新创建的实例,依此类推。

所以我写了这样的东西:

template<typename F>
struct static_filter
{
  template<typename ...Args>
  static std::shared_ptr<F> get(Args... a)
  {
    return _inst = _inst ?: std::shared_ptr<F>(new F(a...));
  }

  static void cleanup()
  {
    // some cleanup operation over inst
    _inst.reset();
  }

private:
  static std::shared_ptr<F> _inst;
};

template<typename F> std::shared_ptr<F> static_filter<F>::_inst{nullptr};

现在我正在寻找一种方法来自动检测何时没有人再使用 _inst。 基本上,每次 _inst 的 use_count() 下降到 1 时,我都想得到一个回调。此时我将能够清理和重置。

我想避免为此实现我自己的引用计数(应用程序到处都在使用 shared_ptr,更改为自定义类型会有点麻烦)。

我尝试在我的实例上使用自定义删除器,大致如下:

template<typename F>
struct static_filter
{
  template<typename ...Args>
  static std::shared_ptr<F> get(Args... a)
  {
    return _inst = _inst ?: std::shared_ptr<F>(new F(a..., **static_filter<F>::cleanup**));
  }

  static void cleanup()
  {
    // some cleanup operation over inst
    _inst.reset();
  }

private:
  static std::shared_ptr<F> _inst;
};

template<typename F> std::shared_ptr<F> static_filter<F>::_inst{nullptr};

但显然这不起作用,因为我的引用计数器实际上从未达到 0。

有谁知道是否有办法使用 shared_ptr 实现此目的?

谢谢。

你在这里使用 shared_ptr 在语义层面上是错误的。您不想拥有此 static 变量的所有权,而只是通过它进行观察。这是 std::weak_ptr 的用例。有了这个静态变量,你可以观察对象,return 如果它存在并且不干扰它的破坏,当其他人持有对它的引用时 return 它作为 shared_ptr

这看起来如下:

template<typename F>
struct static_filter
{
  template<typename ...Args>
  static std::shared_ptr<F> get(Args... a)
  {
    // Returns the shared_ptr observed, if it exists and is not expired, empty ptr otherwise
    // is atomic
    auto shared = _inst.lock(); 
    if(!shared) {
        _inst = (shared = std::shared_ptr<F>(new F(a..., **static_filter<F>::cleanup**));
    }
    return shared;
  }

  static void cleanup()
  {
    // Always check if the calling object is really the managed object
    // some cleanup operation over inst
    _inst.reset();
  }

private:
  static std::weak_ptr<F> _inst;
};

template<typename F> std::weak_ptr<F> static_filter<F>::_inst{};

请注意,这 不是 线程安全的(您之前的代码也不是线程安全的),因为当指针为空时两个同时调用 get,都可以看到指针为空,都构造了一个F类型的新对象。您必须添加一个 std::lock_guard 来避免这种情况。

请注意,即使您可以在 use_count == 1 上添加回调,这本质上也是不安全的。这就是为什么 std::shared_ptr::unique() 从 C++17 开始被弃用并在 C++20 中被删除的原因(参见 here and here