2

在 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(;;);
}

打印以下内容:

在此处输入图像描述

为什么要为线程调用 6 次析构函数?为什么线程报告不同的内存位置?

编辑 添加移动和复制构造函数输出时:

在此处输入图像描述

4

4 回答 4

7

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

于 2012-10-19T23:02:04.287 回答
4

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

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

现在,如果您运行代码,输出(在 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
于 2012-10-19T23:23:02.973 回答
2

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

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

于 2012-10-19T23:01:37.220 回答
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:

C++11 的三规则变成五规则?

于 2012-10-19T23:34:46.687 回答