2013-02-14 34 views
2

考慮一下:的boost :: signals2 - 與插槽的對象descruction

#include <boost/signals2.hpp> 
#include <iostream> 

struct object_with_slot 
{ 
void operator()() 
{ 
    std::cout << "Slot called!" << std::endl; 
    member = 50500; 
} 
int member; 
}; 


int main() 
{ 
boost::signals2::signal<void()> sig; 

object_with_slot * ptr = new object_with_slot; 
sig.connect(*ptr); 

delete ptr; 

sig(); 
} 

輸出是 「叫老虎!」並沒有崩潰或任何東西。這就是爲什麼我有幾個問題:

1)爲什麼沒有崩潰?

2)爲什麼沒有崩潰,即使slot函數將某些東西分配給不存在的對象?

3)我怎樣才能讓信號自動追蹤其插槽的壽命?我的意思是當插槽被破壞時,它會斷開連接。

問題3是最重要的,因爲我需要實現觀察者模式,並且觀察者(插槽)的生命週期不會是靜態的(對於應用程序運行的整個時間)。

+0

請使用信號的值語義。不要將引用推入其中,然後刪除這些引用的來源。沒有理由在這裏分配這個信號對象。 – 2013-02-14 20:17:59

+0

@Nicol Bolas你能解釋一下嗎? – user1873947 2013-02-14 20:35:49

+0

只要做'sig.connect(object_with_slot())'。那麼就沒有生命的問題。有一個原因是爲什麼'sig.connect'需要一個*引用*,而不是一個指針。 – 2013-02-14 20:37:03

回答

3

1)你很幸運。如果沒有,你會得到一個分段錯誤。

2)內存沒有以任何方式覆蓋。

3)您可以使用slot :: track在被跟蹤對象被刪除時自動斷開連接。 Boost.Signals2可以跟蹤由boost :: shared_ptr管理的對象。

#include <boost/signals2.hpp> 
#include <boost/shared_ptr.hpp> 

struct object_with_slot 
{ 
    void operator()() 
    { 
     std::cout << "Slot called!" << std::endl; 
     member = 50500; 
    } 
    int member; 
}; 

// 
int main() 
{ 
    typedef boost::signals2::signal<void()> sig_type; 
    sig_type sig; 

    { 
     boost::shared_ptr<object_with_slot> ptr(new object_with_slot); 
     sig.connect(sig_type::slot_type(*ptr).track(ptr)); 

     // 'object_with_slot' managed by ptr is destroyed 
    } 

    sig(); // 'object_with_slot' not called here. 

    return 0; 
} 

UPDATE:
添加代碼,用於跟蹤對象的std :: shared_ptr的和std :: weak_ptr的:

#include <memory> 
#include <boost/signals2.hpp> 

// added specializations for std::weak_ptr and std::shared_ptr 
namespace boost 
{ 
    namespace signals2 
    { 
    template<typename T> struct weak_ptr_traits<std::weak_ptr<T> > 
    { 
     typedef std::shared_ptr<T> shared_type; 
    }; 

    template<typename T> struct shared_ptr_traits<std::shared_ptr<T> > 
    { 
     typedef std::weak_ptr<T> weak_type; 
    }; 
    } 
} 

struct object_with_slot 
{ 
    void operator()() 
    { 
     std::cout << "Slot called!" << std::endl; 
     member = 50500; 
    } 
    int member; 
}; 

// 
int main() 
{ 
    typedef boost::signals2::signal<void()> sig_type; 
    sig_type sig; 

    std::shared_ptr<object_with_slot> ptr(new object_with_slot); 
    sig.connect(sig_type::slot_type(*ptr).track_foreign(ptr)); // ptr is tracked 

    sig(); 

    return 0; 
} 
+1

不錯,不知道跟蹤功能。 – coelhudo 2013-02-14 20:22:30

+0

@Grigoriy Chudnov是否有任何方式使它與std :: shared_ptr一起工作? – user1873947 2013-02-14 20:34:24

+0

@ user1873947,是的,如果您使用'track_foreign'而不是'track',則可以。但是,您可能需要提供boost :: signals2 :: shared_ptr_traits&boost :: signals2 :: weak_ptr_traits專業化 – 2013-02-14 20:51:34

1

1和2)實際上這是一個未定義的行爲。您使用瞭解引用運算符,現在connect具有object_with_slot的值,其地址可以由內存管理器自由分配給任何其他進程。巧合的是它仍然是一個「有效的地址」。並且ptr可以自由分配給任何其他值而不會造成內存泄漏。

嘗試這樣的事情,你會看到,爆炸每次

#include <boost/signals2.hpp> 
#include <iostream> 

struct object_with_slot 
{ 
    object_with_slot() 
    { 
     member = new int(10); 
    } 

    ~object_with_slot() 
    { 
     delete member; //comment this line and everything works again 
    } 
    void operator()() 
    { 
     std::cout << "Slot called!" << std::endl; 
     *member = 50500; //it was destroyed above 
    } 
    int *member; 
}; 


int main() 
{ 
    boost::signals2::signal<void()> sig; 

    object_with_slot * ptr = new object_with_slot; 
    sig.connect(*ptr); 

    delete ptr; 
    ptr = 0x0; 

    sig(); 
} 

3)你可以把另一個信號上object_with_slot的析構函數,那麼它可以通知時,它被調用。

+0

非常感謝您的回答。我並不認爲3)的答案如此簡單,謝謝。 – user1873947 2013-02-14 20:17:47

0

非常危險的例子中給出。請看:

#include <iostream> 
#include <memory> 
#include <boost/signals2.hpp> 

struct object_with_slot 
{ 
    object_with_slot() { 
     std::cout << "ctor\n"; 
    } 

    object_with_slot(const object_with_slot &) { 
     std::cout << "cctor\n"; 
    } 

    ~object_with_slot() { 
     std::cout << "dtor\n"; 
    } 

    void operator()() 
    { 
     std::cout << "Slot called!" << std::endl; 
     member = 50500; 
    } 
    int member; 
}; 

// 
int main() 
{ 
    typedef boost::signals2::signal<void()> sig_type; 
    sig_type sig; 

    std::shared_ptr<object_with_slot> ptr(new object_with_slot); 
    sig.connect(sig_type::slot_type(*ptr).track_foreign(ptr)); // ptr is tracked 

    sig(); 

    return 0; 
} 

你怎麼認爲,這是什麼代碼了(G ++ 4.8.1,libboost 1.54)?

ctor 
cctor 
cctor 
cctor 
cctor 
cctor 
cctor 
cctor 
cctor 
dtor 
dtor 
dtor 
dtor 
dtor 
cctor 
dtor 
cctor 
dtor 
dtor 
dtor 
cctor 
dtor 
Slot called! 
dtor 
dtor 

我不認爲,這種行爲是預期的。因爲我們將*ptrobject_with_slot的實例)的副本(按值)複製到connect方法。例如,可以通過參考包裝解決:

sig.connect(sig_type::slot_type(boost::ref(*ptr)).track_foreign(ptr)); // ptr is tracked 

小心模板和類型。

相關問題