std :: thread,类构造函数和析构函数

时间:2012-10-19 22:57:14

标签: c++ multithreading c++11 stdthread

在C ++ 11中测试线程时,我创建了以下示例:

#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

4 个答案:

答案 0 :(得分:6)

将移动或复制功能对象。您没有在输出中考虑任何这些。

答案 1 :(得分:3)

添加复制构造函数并将构造函数移动到您的类中。

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

现在如果你运行code输出(在gcc 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设法忽略了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

答案 2 :(得分:2)

因为你的Foo在堆栈上,而不是堆。这意味着你在test_thread中分配一个新的,然后在你调用std :: thread(f)并再次在线程(f)内部时复制它。

您需要创建一个指向堆上的分配的指针,然后传递它,以便每次都不会复制该对象,使用堆(新)来分配它。

答案 3 :(得分: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(;;);
}

它显示所有ctors与dtors配对。

另外看看这个SO:

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