使用std :: weak_ptr的共享资源拥有

时间:2013-08-27 04:20:16

标签: c++ c++11 shared-ptr smart-pointers weak-ptr

我想知道如何(使用C ++ 11并希望与向后(boost或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是否完全合适,因为InputConsumerModelController概念上是拥有,因此它们应该是成员变量。我不知道ModelController仅通过shared_ptr管理它们有多大意义。我无法判断unique_ptr是否与weak_ptr一起使用。我应该只管理shared_ptr的ctor / dtor中的ModelController吗?

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

2 个答案:

答案 0 :(得分:1)

我在共享指针方面没有很多专业知识,但是,这似乎是weak_ptr的恰当用法。

在这种情况下,你只是恼火:

  1. 您希望将InputConsumer直接用作ModelController的成员,因为它是一个微不足道的所有权关系。
  2. 您被迫使用shared_ptr使其与weak_ptr一起使用。
  3. 我认为通过使用shared_ptr作为成员对象的别名来解决这个问题。根据{{​​3}}:

      

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

    我自己从未这样做过,但这似乎适应了你的情况:

    • 拥有InputConsumer
    • ModelController成员
    • 为每个人设置别名shared_ptr
    • 使用InputSender s
    • weak_ptr中引用它们

    修改

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

    #include <iostream>
    #include <memory>
    
    using namespace std;
    
    // A class to try our smart pointers on
    struct Foo 
    {
        Foo() { cout << "constructing Foo\n"; }
        ~Foo() { cout << "destructing Foo\n"; }
    };
    
    // 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 Owner\n"; }
        };
    
        Owner() : self(this, Deleter()) { cout << "constructing Owner\n"; }
        ~Owner()                        { cout << "destructing Owner\n"; }
    };
    
    // 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 Observer\n";
        }
        ~Observer() { cout << "destructing Observer\n"; }
    
        void check()
        {
            if(foo_ptr.expired())
                cout << "foo expired\n";
            else
                cout << "foo still exists\n";
        }   
    };  
    
    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
    

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

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

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

    shared_ptr<Owner> self(this);
    

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

    其余的代码应该用注释自我解释。

答案 1 :(得分:0)

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

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