【问题标题】:std::shared_ptr custom reference counterstd::shared_ptr 自定义引用计数器
【发布时间】:2020-01-31 13:07:56
【问题描述】:

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

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

所以我写了这样的东西:

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};

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

有谁知道是否有办法使用 shared_ptr 实现这一点?

谢谢。

【问题讨论】:

  • 我知道这一点,我正在寻找一种在实际值达到 1 时收到通知的方法。这里的目标是不必手动调用 cleanup() 而是将其触发为只要代码的其他部分没有引用 _inst。
  • 使_inst 成为弱指针而不是共享指针。 get() 执行_inst.lock(),如果锁定失败,则创建一个新的。 F 的析构函数是你的cleanup

标签: c++ shared-ptr


【解决方案1】:

您在此处使用shared_ptr 在语义层面上是错误的。您不想拥有此static 变量的所有权,而只是通过它进行观察。这是std::weak_ptr 的用例。使用这个静态变量,您可以观察对象,如果它存在,则将其作为 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,都可以看到指针是empty 并且都构造了一个F 类型的新对象。您必须添加std::lock_guard 以避免这种情况。

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

【讨论】:

  • 正是我想要的。你的权利,我在这里使用 shared_ptr 是无效的。非常感谢您的帮助:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2016-01-02
  • 1970-01-01
  • 2012-11-17
  • 1970-01-01
相关资源
最近更新 更多