2013-08-27 105 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; 
}