2017-03-02 5 views
0

オーバーロードされた関数 'operator'に 'スレッド'コードを配置すると、デストラクタが最初に呼び出されるように見えます。C++ 11スレッディング:オブジェクトがスレッドに置かれたときにデストラクターが呼び出される理由

これをデバッガで実行すると、文字通りスレッドの最初のステップはdestrを呼び出すことです。

だから、問題は、バットからすぐにdestrを呼び出さずにスレッドにオブジェクトを配置する方法です。

スレッドにオブジェクトを配置する4つの方法を示すプログラムです。それらのすべてはオブジェクトが使用される前にdestrを呼び出します(第4の方法はそれを2回呼び出します)。

注:アプローチ番号3を使用すると、operator()がオーバーロードされた関数でなくても実行されます。

#include <iostream> 
#include <thread> 
#include <unistd.h> 

class MyClass { 
private: int m_id; 
public: 
    //constr 
    MyClass(int id) { m_id=id; std::cout << "constr_" << m_id << "\n";} 

    //destr 
    ~MyClass() { std::cout << "destr_" << m_id << "\n"; } 

    //operator() override for thread 
    void operator()() { 
     while(true) { 
      std::cout << m_id << "\n"; 
      sleep(1); 
     } 
    } 
}; 


int main(int argc, char **argv) { 
    MyClass m1(1); 
    MyClass m2(2); 
    MyClass m3(3); 

    std::thread thread1 { m1 }; 
    std::thread thread2(m2); 
    std::thread thread3(&MyClass::operator(), m3); 
    std::thread thread4{ MyClass(4) }; 

    thread1.join(); 
    return 0; 
} 
+0

値でMyClassのコピーを渡す代わりに、スレッドへのMyClassのインスタンスにスマートポインタ(例:shared_ptr)を渡すことができます。これは、あなたの例のように、main()内のスマートポインタのコピーがスレッドの作成直後にスコープから外れている限り、問題ありません。 – bazza

答えて

1

オブジェクトは、起動時に新しく作成されたスレッドによって破棄されません。スレッドの終了時にオブジェクトのコピーが破棄されます。メインスレッドは、問題のオブジェクトの1つ以上のコピーを作成して破棄します。確認する:

//constr 
MyClass(int id) :m_id(id) { 
    std::cout << " !!! " << m_id << " in " << 
     std::this_thread::get_id() << "\n"; 
} 

//copy constr 
MyClass(const MyClass& other) :m_id(other.m_id) { 
    std::cout << " >>> " << m_id << " in " << 
     std::this_thread::get_id() << "\n"; 
} 

//destr 
~MyClass() { 
    std::cout << " ~~~ " << m_id << " in " 
     << std::this_thread::get_id() << "\n"; 
} 

Live demo

これは、オブジェクトがコピーによってスレッドコンストラクタに渡されるためです。これは、1つのコピーctor/dtorペアを占めます。スレッドコンストラクタは、渡されたオブジェクトの別のコピーをスレッドオブジェクトに格納し、スレッド自体が使用する。このコピーはスレッドの最後で破棄されます。

+0

ありがとう!それは値渡しのパラメータが破壊されたものであったことを意味する – Oscar

2

クラスは値によってスレッドに渡されます。 あなたはnotes on std::thread constructorあなたは以下を参照してください。見れば:

スレッド関数の引数は値で移動またはコピーされます。参照引数をスレッド関数に渡す必要がある場合は、参照引数を(たとえばstd :: refまたはstd :: crefを使用して)ラップする必要があります。

このように、クラスのコピーを作成して渡すために、クラスのデフォルトのコピーコンストラクタが呼び出されます。 あなたのデバッグ出力はあなたに横たわっています。それは間違った値を報告します。

だから、正しい表現を取得するために、コードを実行して、あなたのクラスに次のコピーコンストラクタを追加します。

MyClass(const MyClass& otherr) { m_id=otherr.m_id * 10; std::cout << "constr_" << m_id << std::endl;} 

コピーコンストラクタは、単にIDにあなたができるように、オブジェクトがコピーされるたびにゼロを付加します起こっていることがはっきり分かる。

私が手出力は次のようになります。

constr_1 
constr_2 
constr_3 
constr_10 
constr_100 
destr_10 
constr_20 
constr_200 
destr_20 
constr_30 
constr_300 
100 
200 
destr_30 
constr_4 
constr_40 
constr_400 
300 
destr_40 
destr_4 
400 
200100 

あなたは明確に(10になります)1がコピーされていることを確認することができ、上記(10)、再び(100)にコピーしてから最初のコピーが破壊されたからと2番目のコピーはスレッドで実行されます。

元のオブジェクトは、プログラムの最後でのみ破棄されます。

+0

わかりました。それは正確に何が起こっていたかを非常に明確にするのに役立ちます。ありがとうございました – Oscar

+0

@Oscar、あなたが答えに満足している場合は、それを受け入れるようにマークしてください –

関連する問題