2009-08-13 21 views
4

我在那裏一個boost ::功能和boost ::綁定(實際上是一個std :: TR1 ::功能和綁定)被刪除,而仍在使用的情況。這安全嗎?我通常會避免它,但有問題的代碼有點根深蒂固,我唯一的選擇是添加一個新的線程。刪除提升功能,同時在使用

typedef function<int(int)> foo_type; 

foo_type* global_foo = NULL; 

int actual_foo(int i, Magic* m) 
{ 
    delete global_foo; 
    return m->magic(i); 
} 

int main() 
{ 
    Magic m; 
    global_foo = new foo_type(bind(&actual_foo, _1, &m); 

    return (*global_foo)(10) 
} 

綁定參數始終是純整型(int和實際代碼中的指針),而不是引用。

+0

總結,它可能是危險的,這取決於operator()調用在實現中做了什麼。你不能保證它的功能,所以假設它很危險。 boost :: function的當前實現將是安全的。 – 2009-08-14 16:35:00

回答

5

boost::functionstd::tr1::functions是可複製的對象。因此,通常絕對沒有理由分配它們 - 只是通過價值來傳遞它們。

它們適用於大多數的實際案例優化...所以只是按值傳遞它們:

typedef function<int(int)> foo_type; 

foo_type global_foo; 

int actual_foo(int i, Magic* m) 
{ 
    delete global_foo; 
    return m->magic(i); 
} 

int main() 
{ 
    Magic m; 
    global_foo = bind(&actual_foo, _1, &m); 

    return global_foo(10) 
} 

你建議的代碼是危險的,運行這段代碼:

#include <boost/function.hpp> 
    #include <boost/bind.hpp> 
    #include <iostream> 
    using namespace std; 

    boost::function<void()> *glb; 

    struct Data { 
      int x; 
      Data(int _x = 0) : x(_x) { cout<<"ctor:"<<this<<endl; } 
      ~Data() { cout<<"dtor:"<<this<<endl; } 
      Data(Data const &p) {x=p.x; cout<<"ctor:"<<this<<endl; } 
      Data const &operator=(Data const &p) { x=p.x; cout<<this<<"="<<&p<<endl; return *this; } 
    }; 

    void func(Data const &x) 
    { 
      delete glb; 
      cout<<&x<<endl; 
    } 

    int main() 
    { 
      glb=new boost::function<void()>(boost::bind(func,Data(3))); 

      (*glb)(); 
      return 0; 
    } 

你會發現您嘗試訪問func中的cout<<&x<<endl中已顯示的目標對象(具有相同指針值的指針)已被調用。

因爲當你摧毀你的函數對象,你也破壞了綁定的參數,你 已經和Data const &x,原因是其與global_function

編輯destoryed成爲unavailible: Clearification發表評論:

如果你有什麼像

map<string,function<void()> > calls; 

void delete_key(){ 
    calls.erase("key"); 
} 

main() 
{ 
    calls["key"]=delete_key; 

    // Wrong and dangerous 
    // You delete function while in use 
    calls["key"](); 

    // Correct and safe 
    // You create a copy of function so it would not 
    // be deleted while in use. 
    function<void()> f=calls["key"]; 
    f(); 
} 
+1

很確定你不想刪除global_foo了。 – Salgar 2009-08-13 20:24:51

+0

看起來我太簡化了這個問題。 global_foo按值存儲在地圖中。在調用地圖時,可以從地圖上刪除<>功能。我知道如果參數的生命週期不能保證(struct Data),那將是危險的。但我試圖清楚它的綁定參數是不可或缺的類型 – 2009-08-13 21:08:37

2

編輯:討論了這個答案多一點(與查爾斯貝利),我認爲這是不安全的,因爲它取決於實施boost :: function。

當我們進入actual_foo會是這個樣子的調用堆棧:

actual_foo 
boost::function::operator() 
main 

所以當actual_foo執行完畢,我們會跳回的boost::functionoperator()代碼,這個對象已被刪除。這不能保證是一個問題 - 這有點像調用delete this - 但你必須非常小心你在刪除對象的成員函數中所做的事情。您不允許調用任何虛擬功能或使用任何數據成員。

問題是,我不知道boost::function對它在調用函數後調用operator()時做了什麼保證。在我的平臺上它似乎沒有做任何危險的事情(所以valgrind沒有抱怨),但完全可以想象,在不同的平臺,不同的實現,或不同的編譯標誌,它可能想做的事情是不是一旦對象被刪除就不允許 - 例如,它可能使用成員變量寫出一些調試信息。

因此,我認爲這是做可能會導致在某些情況下不確定的行爲很危險的事。

進一步注:

我參加了一個快速瀏覽一下升壓真的做得它調用函數指針之後。在這裏尋找:http://boost.cvs.sourceforge.net/viewvc/boost/boost/boost/function/function_template.hpp?view=markup在687行的operator()函數中,我的解釋是它立即返回返回值,並且什麼也不做,所以在實踐中,通過這個實現,你應該沒問題,但是關於它的評論危險仍然存在。請注意,我很可能已經發現的代碼錯誤位和/或理解錯了......

原文如下答案:

我相信這是好的。通過輸入actual_foo時,boost::bindboost::function對象已經完成了自己的工作,而你正在執行的實際功能actual_foo

我在我的平臺(gcc 4.2.4,Linux)上通過運行程序valgrind檢查了這一點。

這裏是我跑的程序:

#include <boost/bind.hpp> 
#include <boost/function.hpp> 

class Magic 
{ 
public: 
    int magic(int i) 
    { 
     return 5; 
    } 
}; 

typedef boost::function<int(int)> foo_type; 

foo_type* global_foo = NULL; 

int actual_foo(int i, Magic* m) 
{ 
    delete global_foo; 
    return m->magic(i); 
} 

int main() 
{ 
    Magic m; 
    global_foo = new foo_type(boost::bind(&actual_foo, _1, &m)); 

    return (*global_foo)(10); 
} 

這裏是Valgrind的輸出:

$ valgrind ./boost_bind 
==17606== Memcheck, a memory error detector. 
==17606== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al. 
==17606== Using LibVEX rev 1804, a library for dynamic binary translation. 
==17606== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP. 
==17606== Using valgrind-3.3.0-Debian, a dynamic binary instrumentation framework. 
==17606== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al. 
==17606== For more details, rerun with: -v 
==17606== 
==17606== 
==17606== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 17 from 1) 
==17606== malloc/free: in use at exit: 0 bytes in 0 blocks. 
==17606== malloc/free: 1 allocs, 1 frees, 16 bytes allocated. 
==17606== For counts of detected errors, rerun with: -v 
==17606== All heap blocks were freed -- no leaks are possible. 

我必須說,雖然,這似乎是一個奇怪的事情。如果可能的話,我更願意使用自動刪除此函數對象,方法是將其設置爲堆棧變量,或者在堆棧變量的析構函數中將其刪除(如RAII)。它會更整潔,更安全,不那麼可怕,並且更加安全。但我相信你已經知道了這一切,並且有充分的理由與代碼一起生活。

+0

-1這是不正確的 - 代碼是危險的 – Artyom 2009-08-13 20:08:18

+0

請解釋它爲什麼是危險的。你的意思是不明智的(我同意),或者你的意思是它導致了未定義的行爲(我還沒有說服)? – 2009-08-14 08:28:12

+0

我注意到你的(Artyom's)在你的回答中指出,如果綁定對象已被刪除,則actual_foo的參數可能已被刪除。然而,由於參數在這裏被傳遞的價​​值,我不認爲這是事實。我是一個int的本地副本,而m是指向在main()中初始化的堆棧變量的指針的本地副本。 – 2009-08-14 08:32:13