2012-10-19 166 views
2

當C++ 11我已經創建了下面的例子測試線程:的std ::線程類的構造函數和析構函數

#include <iostream> 
#include <thread> 

class Foo { 
public: 
    Foo(void) { 
     std::cout << "Constructor called: " << this << std::endl; 
    } 
    ~Foo(void) { 
     std::cout << "Destructor called: " << this << std::endl; 
    } 
    void operator()() const { 
     std::cout << "Operatior called: " << this << std::endl; 
    } 
}; 

void test_normal(void) { 
    std::cout << "====> Standard example:" << std::endl; 
    Foo f; 
} 

void test_thread(void) { 
    std::cout << "====> Thread example:" << std::endl; 
    Foo f; 
    std::thread t(f); 
    t.detach(); 
} 


int main(int argc, char **argv) 
{ 
    test_normal(); 
    test_thread(); 

    for(;;); 
} 

打印出以下幾點:

enter image description here

爲什麼析構函數調用了6次線程?爲什麼線程報告不同的內存位置?

編輯 當添加移動和複製構造函數輸出:

enter image description here

回答

3

添加複製構造函數並將構造函數移動到您的類中。

Foo(Foo const&) { std::cout << "Copy Constructor called: " << this << std::endl; } 
Foo(Foo&&) { std::cout << "Move Constructor called: " << this << std::endl; } 

現在,如果你運行code輸出(海合會4.7.2)看起來是這樣的:

====> Standard example: 
Constructor called: 0xbff696ff 
Destructor called: 0xbff696ff 
====> Thread example: 
Constructor called: 0xbff696ff 
Copy Constructor called: 0xbff696cf 
Move Constructor called: 0x93a8dfc 
Destructor called: 0xbff696cf 
Destructor called: 0xbff696ff 
Operator called: 0x93a8dfc 
Destructor called: 0x93a8dfc 

正如你所看到的,調用析構函數的號碼匹配的呼叫數到各種構造函數。

我懷疑GCC管理的Elid到幾個拷貝/移動建設的調用MSVC似乎是使,所以有較少的電話比你的例子來析構函數。


此外,還可以通過std::move完全避免拷貝構造荷蘭國際集團的Foo對象的線程構造函數。

test_thread變化的線構造線

std::thread t(std::move(f)); 

現在輸出看起來是這樣的:

====> Standard example: 
Constructor called: 0xbfc23e2f 
Destructor called: 0xbfc23e2f 
====> Thread example: 
Constructor called: 0xbfc23e2f 
Move Constructor called: 0xbfc23dff 
Move Constructor called: 0x9185dfc 
Destructor called: 0xbfc23dff 
Destructor called: 0xbfc23e2f 
Operator called: 0x9185dfc 
Destructor called: 0x9185dfc 
+1

我明白了。我現在把我的輸出添加到了帖子中。看起來我有3個額外的移動構造函數調用。這與編譯器有關嗎? – toeplitz

+0

會有當你創建'Foo'一個對象調用構造,當您將它傳遞給線程構造一個拷貝構造調用。其餘的與實施有關。 – Praetorian

2

因爲你富是在棧上,而不是堆。這意味着你在test_thread裏面分配一個新的,然後當你調用std :: thread(f)並且再次在thread(f)內時它被複制。

您將需要而是創建一個指向在堆上分配和傳遞,使對象每次不復制,使用堆(新)來分配它。

+0

林不知道我的理解。我以爲它只會被複制一次? – toeplitz

+0

將值傳遞給線程構造函數時將其複製,並由線程構造函數將其複製到基線中。這意味着您將首先刪除test_thread和test_normal中的副本,然後是線程構造函數中的副本,然後是線程本身內的副本。 – Jake

+0

我明白在線程測試中有兩個破壞調用。但還有4個? – toeplitz

6

函數對象將被移動或複製。你沒有在你的輸出中記錄任何這些。

1

編譯器添加默認的移動和複製構造函數,如果你不自己做,看看這個

https://ideone.com/wvctrl

#include <iostream> 
#include <thread> 

class Foo { 
public: 
    Foo(Foo&& f) { 
     std::cout << "Constructor Foo&& called: " << this << std::endl; 
    } 
    Foo(const Foo& f) { 
     std::cout << "Constructor const Foo& called: " << this << std::endl; 
    } 
    Foo(void) { 
     std::cout << "Constructor called: " << this << std::endl; 
    } 
    ~Foo(void) { 
     std::cout << "Destructor called: " << this << std::endl; 
    } 
    void operator()() const { 
     std::cout << "Operatior called: " << this << std::endl; 
    } 
}; 

void test_normal(void) { 
    std::cout << "====> Standard example:" << std::endl; 
    Foo f; 
} 

void test_thread(void) { 
    std::cout << "====> Thread example:" << std::endl; 
    Foo f; 
    std::thread t(f); 
    t.detach(); 
} 


int main(int argc, char **argv) 
{ 
    test_normal(); 
    test_thread(); 

    for(;;); 
} 

它表明,所有構建函數配對dtors。

還應考慮該SO:

Rule-of-Three becomes Rule-of-Five with C++11?