2

When testing threads in C++11 I have created the following example:

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

Which prints the following:

enter image description here

Why is the destructor called 6 times for the thread? And why does the thread report different memory locations?

EDIT When adding move and copy constructor output:

enter image description here

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 が行っていると思われる copy/move 構築呼び出しのいくつかをなんとか回避していると思われるため、デストラクタへの呼び出しはあなたの例よりも少なくなります。


さらに、オブジェクトをスレッドコンストラクターに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(;;);
}

すべての ctor が dtor とペアになっていることがわかります。

このSOも調べてください:

ルール・オブ・スリーは C++11 でルール・オブ・ファイブになる?

于 2012-10-19T23:34:46.687 に答える