是否有需要多个所有者的智能指针类型?

时间:2016-08-24 10:37:46

标签: c++ c++11 smart-pointers

当引用计数达到0时,std::shared_ptr会破坏它正在管理的对象。但是,我正在寻找一种智能指针,当引用计数低于2时,对象会被销毁。是否有智能表现得像这样的指针(或者我可以以安全的方式使智能指针表现得像这样)?

用例场景:我正在建模连接。连接由它连接的两个端点拥有(如“智能指针所有权”)。只要其中一个端点被破坏,连接也应该被销毁。

我知道我可以在适当的析构函数中使用经典的delete语句来实现这一点(因为在这种情况下我的“低于2”的要求非常简单)。但我认为这是一种智能指针的有效用例,我很想知道我是否可以用现代方式做到这一点。

3 个答案:

答案 0 :(得分:4)

最简单的解决方案可能是每一方都有shared_ptr对象,weak_ptr对象,以及指向对方shared_ptr的常规指针。

要访问该对象,请锁定weak_ptr。如果失败,则对象消失。

为了摧毁你自己,你锁定weak_ptr,通过你的常规指针重置另一边的shared_ptr,重置你自己的shared_ptr,然后摆脱锁定的结果在weak_ptr

或者,您可以使用计数器和常规指针。如果计数器为1,你知道另一面已经消失,所以你可以破坏对象。

答案 1 :(得分:4)

感谢你包括你想要达到的效果。

您不希望或需要智能指针中的任何特殊逻辑。两端都需要对共享对象的普通强引用。只要双方都知道它就会干净地表达它。

你想要的是一个事件通知,任何一方都可以在离开时通知另一方。然后幸存的一方可以干净地进行适当的清理,包括将其(现在最后一次)shared_ptr设置为null,还要做其他逻辑上需要的事情。

答案 2 :(得分:1)

由于你有两个所有者,当你们要死的时候你想要杀死它,我可能会做这样的事情:

template<class T>
struct shared_connection {
  // by default, kill the connection if we are connected:
  ~shared_connection() {
    sever();
  }
  // wrap a shared pointer to the connection data:
  shared_connection( std::shared_ptr<T> p ):
    ptr(std::make_shared<std::shared_ptr<T>>(std::move(p)))
  {}
  // create a new connection:
  shared_connection fork() const {
    return {ptr};
  }
  // an even more explicit move:
  shared_connection transfer() {
    return std::move(*this);
  }
  friend void swap( shared_connection& lhs, shared_connection& rhs ) {
    std::swap( lhs.ptr, rhs.ptr );
  }
  // move only type:
  shared_connection(shared_connection&& src)
  {
    swap(*this, src);
  };
  shared_connection& operator=(shared_connection&& src)
  {
    auto tmp = std::move(src);
    swap(*this, tmp);
    return *this;
  };
  // lock it for use.  The connection can die
  // but the data will persist until the lock ends:
  std::shared_ptr<T> lock() const {
    if (!ptr) return {}; // don't break if we have been abandon()ed
    return atomic_load(ptr.get());
  }
  // do not kill the connection:
  void abandon() {
    ptr = {};
  }
  // kill the connection:
  void sever() {
    atomic_store(ptr.get(), std::shared_ptr<T>{});
    abandon();
  }
  // please just lock instead of calling this:
  explicit operator bool() const {
    return (bool)lock();
  }
private:
  std::shared_ptr<std::shared_ptr<T>> ptr;
};
template<class T, class...Args>
shared_connection<T> make_shared_connection( Args&&... args ) {
  return std::make_shared<T>(std::forward<Args>(args)...);
}

首先,通过shared_connection创建std::make_shared<T>

然后.fork()给另一方。当 shared_connection的任何消失时,内部T将被销毁,除非其中一个shared_connection拥有它{{ 1}}编

我认为我的原子代码是正确的,所以它支持连接的两个端口在不同的线程中。它还应该支持多方连接。

第一个共享指针表示对连接的未锁定生存期的共享控制。第二个共享指针表示在短暂操作期间保持连接的能力。

.lock()允许某人在不中断连接的情况下断开连接。 .abandon()允许您在不破坏对象的情况下销毁共享连接数据。

代码未经测试。

如果您希望连接的每一方在共享连接的末尾共享一堆代码中的所有权,请执行指向该共享连接的共享指针。因为没有什么能像共享指向共享指针的共享指针那样爱。

我们还可以通过在应用程序中阻止对它的访问来避免内部共享指针“泄漏”的可能性:

.sever()

用上述方法替换公共template<class F, class R=std::decay_t<std::result_of_t<F(T const&)>>> std::optional<R> read( F&& f ) const { auto p = lock(); if (!p) return {}; T const& t = *p; return std::make_optional<R>( std::forward<F>(f)(t) ); } template<class F, class R=std::decay_t<std::result_of_t<F(T&)>>> std::optional<R> write( F&& f ) const { auto p = lock(); if (!p) return {}; T& t = *p; return std::make_optional<R>( std::forward<F>(f)(t) ); }