std::thread,类构造函数和析构函数
Posted
技术标签:
【中文标题】std::thread,类构造函数和析构函数【英文标题】:std::thread, class constructor and destructor 【发布时间】:2012-10-19 22:57:13 【问题描述】:在 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 次析构函数?为什么线程会报告不同的内存位置?
编辑 添加移动和复制构造函数输出时:
【问题讨论】:
【参考方案1】:函数对象将被移动或复制。您没有在输出中考虑任何这些。
【讨论】:
【参考方案2】:添加一个复制构造函数并将构造函数移动到您的类。
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
【讨论】:
我明白了。我现在将我的输出添加到帖子中。看起来我有 3 个额外的移动构造函数调用。这和编译器有关吗? 当你创建Foo
对象时会有一个构造调用,当你将它传递给线程构造函数时会有一个复制构造调用。其余的是与实现相关的。【参考方案3】:
因为您的 Foo 在堆栈上,而不是在堆上。这意味着您在 test_thread 中分配一个新的,然后在您调用 std::thread(f) 并再次在 thread(f) 中复制它。
您需要创建一个指针以在堆上分配,并传递它以便每次都不会复制对象,使用堆(新)分配它。
【讨论】:
我不确定我是否理解。我以为它只会被复制一次? 将值传递给线程构造函数时复制,线程构造函数将其复制到基中。这意味着您将首先删除 test_thread 和 test_normal 中的副本,然后是线程构造函数中的副本,然后是线程本身中的副本。 我了解线程测试中的两个销毁调用。但是还有4个?? 您的类没有正确的复制或移动构造函数。您会看到额外的,因为有些来自普通的 foo,实际上您首先有一个构造函数,然后是一个副本,然后是一个移动。那是 3。基本输出至少还有 1 个。【参考方案4】:编译器会添加默认的移动和复制构造函数,如果你自己不做,检查一下
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?
【讨论】:
以上是关于std::thread,类构造函数和析构函数的主要内容,如果未能解决你的问题,请参考以下文章