只与一个所有者进行shared_ptr

Make a shared_ptr with only one owner

本文关键字:shared ptr 所有者 一个      更新时间:2023-10-16

我在 C++11 中有一个只有一个所有者的对象。但是,其他对象可以保存此对象的weak_ptr。这样,我可以在使用对象之前测试对象是否仍然存在。

目前,所有者具有对象的shared_ptr,该对象的所有者计数始终为 1。

如何确保不允许任何人复制此shared_ptr

我不能使用unique_ptr,因为显然 C++11 不允许用unique_ptr进行weak_ptr.

编辑:感谢您的回复!

所有程序都在单个线程上运行。主所有者有一个定期更新循环,在其中调用其所有子系统。

感谢您指出使用weak_ptr实际上会创建一个shared_ptr。预期是不允许任何子系统或对象在调用另一个非 const 函数期间存储shared_ptr。这样,任何函数都可以要求所有者从其集合中删除对象,并立即删除该对象。

我希望避免每次子系统调用lock()时增加shared_ptr的引用计数的开销,因为它有望立即释放所有权(并确保在编译时是这种情况(。

编辑2:示例代码以更清楚地表达我的问题

class Manager {
  public:
    Manager();           //Creates objects and stuff
    void updateAllObjects() {
      for (auto& o : mObjects)
        o->update(*this);
    }
    void deleteObject(weak_ptr<WorldObject>);    //May be called by other objects
    vector<shared_ptr<WorldObject>> mObjects;
};
class WorldObject {
  public:
    virtual void update(Manager&)=0;
};
class Passenger : public WorldObject {
  public:
    void update(Manager&);
};
class Car : public WorldObject {
  public:
    void update(Manager&);    //May call Manager::deleteObject on its passengers, or change them etc...
    //If Manager decides to delete passenger, they must be destroyed, so we don't want those references to count
    vector<weak_ptr<Passenger>> mPassengers;
    //A passenger can belong to several cars, and must be updated exactly once per WorldObject::update()
};

如何:- 避免存储许多shared_ptr的开销- 确保Car不会将乘客的shared_ptr存储时间超过其Car::update()功能

目前,所有者对对象有一个shared_ptr,该对象始终具有 所有者计数为 1。

这是一个错误的陈述,因为std::weak_ptr的所有者只能通过创建std::shared_ptr来使用它,因此所有者计数将> 1。所以你的问题几乎没有意义 - 如果你只想提供对对象移动std::unique_ptr的独占访问权限,如果你想通过std::weak_ptr使用它,它必须被共享,原则上你不能强制单一所有者。

你不能

shared_ptr的本质是你可以创建更多的实例。你能做的,就是让它成为班级的私人成员,不要像现在这样直接将shared_ptr暴露给外部世界。但是,对于使用哑巴来说,您不能安全,有人也可以获得weak_ptr,提取原始指针并将其删除。如果 yoru 类的用户想搬起石头砸自己的脚,你无能为力!

如果不

实现自己的valid_ptr或类似的东西,你就无法做到这一点,因为你总是可以通过weak_ptr::lock获得另一个shared_ptr。也就是说,仅当您要定期检查对象是否被删除时(即不用于检查对象是否仍然存在或实际访问它,请参见下文!

template<typename T>
class valid_ptr {
public:
     template<typename S>
     valid_ptr(const std::weak_ptr<S>& r) : m_ptr(r) { }
     bool expired() const { return m_ptr.expired(); }
private:
     std::weak_ptr<T> m_ptr;
};

如果你真的想访问你的指针,那么你应该简单地使用它的weak_ptr,并通过weak_ptr::lock在访问期间获取一个shared_ptr:

if (auto shared = weak.lock()) {
    // ptr is valid and can now be used via the shared_ptr shared
} else {
    // ptr is nullptr
}

您不应该简单地依靠肯定的expired检查来访问对象,因为在检查和访问之间可能会发生任何事情(包括删除对象,除非您有一些您在问题中未提及的其他约束(。在这种情况下,您希望通过在访问期间获取shared_ptr来确保(共享(所有权。

不要这样做。

如果您必须:

将共享 ptr 用于包含包装可写唯一 ptr 的互斥锁函子。

其他人都获得一个共享的 ptr 到一个互斥包装函子,该函子包装一个可能为空的不可变原始 ptr,通过包装上述内容并在 apply 中公开.get()结果来实现。

所有者函子由一位代码拥有。 它公开非所有者函子。 它可以破坏独特的ptr。

非所有者函子不能将对象的生存期延长到超过其互斥锁。

所有者函子可以将唯一的 ptr 设置为无,并且(一旦他们有了互斥锁(没有人可以阻止它们。

您可以使用共享互斥锁来允许多个读取器。

我所说的函子是什么意思?

函子是包装另一个支持 apply 的类型的类型。 如果 [T] 是函子类型,并且T包装的类型,并且 T->U 是接受T并返回U的函数的类型,则 apply 操作的类型为:

([T],(T->U))->U

[T].apply(T->U)

返回一个U

如果T是一种类型,则将T->U应用于T以获得U

如果[T]T的函子,则将其应用于T->U以获得U。 (请注意,与上一段相比,措辞略有变化(。

T的函子知道如何运行T函数,但没有声称自己是T

在实现方面,它要么包含一个T,要么知道如何在询问时获得一个。 它可以对如何运行函数做出其他保证(例如,用互斥锁保护,在专用线程中,在服务器上,等等(。

参见Haskell了解更多信息。

另请注意,合理的公理不支持重入,因此单个线程上的死锁是可能的。

这个答案不容易理解:我只是为了完整而包括它。 除非你来自现代函数式编程背景C++,否则任何问OP问题的人都可能不会得到这个,也不应该尝试(我只是想证明OP的请求是可能的,nkt这是一个好主意(。 更糟糕的是,如果不是设计,我可能弄错了一些函数式编程术语。

如何使用 std::unique_ptr 指向对象,而不是 std::weak_ptr 指向此 std::unique_ptr 的原始指针。这样,您只有一个对象的所有者,并且您可以使用 bool 运算符通过指向它的原始指针测试 std::unique_ptr 的有效性。

auto object = std::make_unique<T>(...); // creates object and std::unique_ptr
...
auto* object_user = &object; // raw pointer to the std::unique_ptr holding the object
...
if ( *object_user ) (*object_user)->... // using the object

注意:省略号是您需要的任何代码。