使用 std::weak_ptr 共享资源所有权

Shared resource ownage using std::weak_ptr

本文关键字:共享资源 所有权 ptr weak std 使用      更新时间:2023-10-16

我想知道如何(使用 C++11 并希望使用向后(升压或 TR1(兼容的智能指针类型(实现:

一个类实例(ModelController(拥有资源(InputConsumer(,而另一个组件(InputSender,在本例中是单例(可以访问它。

该模型InputSender包含对InputConsumers的引用列表,其中会有很多。

ModelController可能没有、一个或多个InputConsumers,并且可能有很多ModelControllerInputSender不知道。

这是

很好的:InputSender跟踪分配给它的InputConsumers的方法,这样它就可以自己找出单个InputConsumers是否有效。

在我看来,weak_ptr非常适合此目的,因为它们的使用需要检查此条件。

如果InputSender停止跟踪其任何weak_ptr引用,则不会发生任何坏事,相应的InputConsumer只会经历无线电静默。

如果删除了ModelController,或者ModelController删除了它的某些InputConsumer,则在下次尝试访问它们时,任何已注册InputSender都将识别出它们不再存在,并且可以清理,而无需发送消息或执行任何操作。

所以问题是,这是使用shared_ptrweak_ptr的合适情况吗?我想知道shared_ptr是否完全合适,因为InputConsumer在概念上由其ModelController拥有,因此它们应该是成员变量。我不知道ModelController只通过shared_ptr管理它们有多大意义。我无法判断unique_ptr是否与weak_ptr一起工作.我应该只管理ModelController的 ctor/dtor 中的shared_ptr吗?

也可能有一个众所周知的(不是我!(设计模式,所以如果有人知道这样的事情,请告诉我。

我在共享指针方面没有很多专业知识,但是是的,这似乎是对weak_ptr的非常合适的使用。

在这种情况下,您只是对以下情况感到恼火:

  1. 您希望直接使用 InputConsumer 作为 ModelController s 的成员,因为这是一个微不足道的所有权关系。
  2. 您被迫使用shared_ptr使其与weak_ptr一起使用。

我认为这可以通过使用shared_ptr作为成员对象的别名来解决。根据C++.com:

此外,shared_ptr对象可以共享指针的所有权 同时指向另一个对象。这种能力是 称为别名(请参阅构造函数(,通常用于指向 成员对象,同时拥有它们所属的对象。

我自己从来没有做过,但这似乎适合你的情况:

  • 拥有InputConsumer ModelController 的成员
  • 为它们中的每一个都有一个别名shared_ptr
  • 使用 weak_ptrInputSender中引用它们

编辑

这是一个完整的最小工作示例:

#include <iostream>
#include <memory>
using namespace std;
// A class to try our smart pointers on
struct Foo 
{
    Foo() { cout << "constructing Foon"; }
    ~Foo() { cout << "destructing Foon"; }
};
// A class that owns some Foo as members
struct Owner
{
    // The actual members
    Foo foo1;
    Foo foo2;
    // A fake shared pointer whose purpose is:
    //   1) to be of type shared_ptr<>
    //   2) to have the same lifetime as foo1 and foo2
    shared_ptr<Owner> self;
    // A fake deleter that actually deletes nothing 
    struct Deleter
    {
        void operator() (Owner *) { cout << "pretend to delete Ownern"; }
    };
    Owner() : self(this, Deleter()) { cout << "constructing Ownern"; }
    ~Owner()                        { cout << "destructing Ownern"; }
};
// A class that holds a reference to a Foo
struct Observer
{
    // A reference to a Foo, as a weak pointer
    weak_ptr<Foo> foo_ptr;
    Observer(const shared_ptr<Foo> & foo_ptr) : foo_ptr(foo_ptr)
    {
        cout << "constructing Observern";
    }
    ~Observer() { cout << "destructing Observern"; }
    void check()
    {
        if(foo_ptr.expired())
            cout << "foo expiredn";
        else
            cout << "foo still existsn";
    }   
};  
int main()
{
    // Create Owner, and hence foo1 and foo2
    Owner * owner = new Owner;
    // Create an observer, passing an alias of &(owner->foo1) to ctor
    Observer observer(shared_ptr<Foo>(owner->self, &(owner->foo1)));
    // Try to access owner->foo1 from observer
    observer.check();
    delete owner;
    observer.check();
    return 0;
}

它打印:

constructing Foo
constructing Foo
constructing Owner
constructing Observer
foo still exists
destructing Owner
pretend to delete Owner
destructing Foo
destructing Foo
foo expired
destructing Observer

棘手的部分是能够创建一个要owner->foo1weak_ptr(对于foo2也是如此(。为此,我们首先需要一个别名shared_ptr owner->foo1 .这只能通过以下方式完成:

shared_ptr<Foo> alias(other_shared_ptr, &(owner->foo1));

其中other_shared_ptr是一个shared_ptr<T>,其寿命至少与owner->foo1的寿命一样长。为此,使用也是owner成员的shared_ptr<T>是一个好主意,因为它可以确保生命周期相同。最后,我们需要一个有效的非空指针来提供给它,并且由于我们不想在堆上创建任何内容,因此我们必须使用现有对象。 this是一个很好的候选者,因为我们知道它是有效的,只有在其成员被摧毁后才会被销毁。因此,我们的other_shared_ptr例如:

shared_ptr<Owner> self(this);

但是,这意味着当self超出范围时,即在销毁owner期间,它将调用delete this。我们不希望发生这种删除,否则this将被删除两次(这是未定义的行为,实际上是段错误(。因此,我们还向self的构造函数提供了一个实际上不会删除任何内容的删除器。

代码的其余部分应该与注释不言自明。

只是一个更完整的工作片段,显示 std::weak_ptr 动态,也许可以提供更多帮助。(我特别喜欢这个过期的((语义(;

#include<iostream>
#include<memory>
#include<string>
class MessageProcessor{                
};
class Message{
public:
        Message(std::shared_ptr<MessageProcessor>  _msg_proc, int _id){
                proc_weak = std::weak_ptr<MessageProcessor>(_msg_proc);
                proc_shared = _msg_proc;
                id = _id;
        }
        std::weak_ptr<MessageProcessor> proc_weak;
        std::shared_ptr<MessageProcessor> proc_shared;        
        int id;
};
int main(){
        std::shared_ptr<MessageProcessor> proc(new MessageProcessor());
        Message msg(proc,1);
        // Here we have proc with 2 shared_ptr refs: 'proc' and 'msg.proc_shared'
        // As expected 'msg.proc_weak is not expired'
        if( !msg.proc_weak.expired() )
                std::cout << "1) proc_weak is not EXPIRED. proc.use_count() == " << proc.use_count() << std::endl;        
        // make one of shared_ptr ref, point to other place
        msg.proc_shared = std::shared_ptr<MessageProcessor>();        
        // there is still the 'proc' reference        
        if( !msg.proc_weak.expired() )
                std::cout << "2) proc_weak is not EXPIRED (yet). proc.use_count() == " << proc.use_count() << std::endl;
        // 'erase' the last reference
        proc = std::shared_ptr<MessageProcessor>(); 
        // Finally... There is no more refs in shared_pointer!
        if( msg.proc_weak.expired() )
                std::cout << "3) proc_weak has EXPIRED. proc.use_count() == " << proc.use_count() << std::endl;
        return 0; 
}