2013-08-27 59 views
3

我想了解一下如何(使用C++ 11,并希望与向后(升压或TR1)兼容的智能指针类型)实现:共享资源OWNAGE使用std :: weak_ptr的

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

该模型是InputSender持有引用InputConsumers,其中会有很多的列表。

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

以下是最好的方法:InputSender跟踪分配给它的InputConsumers,以这样一种方式,它可以找出个人InputConsumers是否有效。

在我看来,weak_ptr是完美的,因为他们的使用需要检查这种情况。

如果InputSender停止跟踪其任何weak_ptr裁判,没有什么不好的事情发生,相应的InputConsumer将只是体验无线电沉默。

如果ModelController被删除,或者如果ModelController删除一些InputConsumer S的,这与他们注册的任何InputSender旨意认识到在他们试图访问他们,他们不再存在接下来的时间,并且可以清理,而不需要发送消息或做任何事情。

所以问题是,这是一个适当的情况下使用shared_ptrweak_ptr?我想知道shared_ptr是否完全合适,因为InputConsumer在概念上是拥有ModelController s,所以它们应该是成员变量。我不知道ModelController只能通过shared_ptr来管理它们。我不知道unique_ptr是否与weak_ptr一起工作。我是否应该管理ModelController的ctor/dtor中的shared_ptr

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

回答

1

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

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

  1. 你想用InputConsumer小号直接为ModelController小号成员,因为它是一个微不足道的所有权关系。
  2. 您被迫使用shared_ptr使其与weak_ptr一起使用。

我认为这是通过使用一个shared_ptr作为别名一个成员对象的解决。根据C++.com

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

我从来没有它自己,但这似乎适用于您的情况:

  • ModelController小号
  • InputConsumer小号成员有一个别名shared_ptr为他们每个人的
  • 参考他们InputSender使用weak_ptr s

编辑

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

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

然而,这意味着,当selfowner销毁期间失控的范围,即,它会调用delete this。我们做不是想要这个删除发生,否则this会被删除两次(这是未定义的行为,实际上是段错误)。因此,我们还向self的构造函数提供了一个实际上不会删除任何东西的Deleter。

其余的代码应该是不言自明的。

+0

这听起来很酷,但它不是立即清楚如何去消除它们!你能发表一个小例子吗?谢谢! –

+0

@StevenLu我以前从来没有使用过别名,所以我不得不学习细节,而且实际上我预料会更棘手。无论如何,这是值得的努力,现在我理解他们很好,并有一个工作示例,我与你分享(见编辑)。另一种方法是不使用这些别名,并直接使用'Owner():foo1_ptr(new Foo){}'初始化'Owner'的'shared_ptr foo1_ptr'成员,然后直接使用'Observer observer(owner-> foo1_ptr)',但是这会在您希望避免的堆上创建数据。 – Boris

+0

太棒了!谢谢。顺便说一句好的网站! –

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