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::moveFoo 对象传递给线程构造函数来完全避免复制构造。

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,类构造函数和析构函数的主要内容,如果未能解决你的问题,请参考以下文章

php构造函数的PHP 5 构造函数和析构函数

C语言里面构造函数和析构函数的运用办法

构造函数和析构函数

构造函数和析构函数能不能被继承

CPP_类默认函数:构造函数,拷贝构造函数,赋值函数和析构函数

C++面试之 类string的构造函数拷贝构造函数和析构函数