2012-10-19 164 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?