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

Is there a smart pointer type that requires multiple owners?

本文关键字:智能 指针 类型 所有者 存在 是否      更新时间:2023-10-16

当引用计数达到0时,std::shared_ptr会破坏它正在管理的对象。然而,我正在寻找一种类型的智能指针,当引用计数降至2以下时,对象会被销毁。有没有一个智能指针会这样做(或者我可以以安全的方式让智能指针这样做吗(?

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

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

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

要访问对象,请锁定weak_ptr。如果失败,则对象不存在。

要摧毁你自己,你锁定weak_ptr,通过你的常规指针重置对方的shared_ptr,重置你自己的shared_ptr,然后摆脱对weak_ptr的锁定结果。

或者,您可以只使用一个计数器和一个常规指针。如果计数器为1,则表示对方已不在,因此可以直接销毁该对象。

感谢您提供您想要达到的效果。

您不希望也不需要智能指针中的任何特殊逻辑。两端都需要对共享对象的普通强引用。这清楚地表明,只要任何一方知道它,它就会活下去

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

因为你有两个所有者,当其中一个死亡时,你想杀死它,我可能会做这样的事情:

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

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

然后.fork()将其发送给另一方。当任何shared_connections消失时,内部T被破坏,除非其中一个shared_connections具有.lock()ed。

我认为我的原子代码做对了,所以它支持连接的两侧在不同的线程中。它还应该支持多方连接。

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

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

通过用上述方法替换公共CCD_ 20。