2017-09-18 6 views
1

펑터가 thread으로 전달 될 때 펑터의 분해 생성기 (즉, () 연산자가있는 클래스)가 호출 될 때를 이해하려고합니다. 그러나 다음 예제에서 생성자는 한 번 호출되지만 deconstructor는 세 번 호출됩니다. 누락 된 무엇펑터의 스레드 및 해체

  • (2 개)에 따르면

    생성자 호출? 복사 또는 이동합니까?

  • 이동 생성자가 호출되면 이동되는 리소스를 파괴하지 않도록 생성자를 어떻게 작성할 수 있습니까? 파일 run()을 열고 ~run()에 닫으면 3 번 호출하면 문제가 발생한다고 가정 해 보겠습니다.

예 : 5, 3 및 제로

in run() 
in ~run() 
in ~run() 
in ~run() 
+0

'thread t (std :: ref (thread_r));'을 시도하십시오. [데모] (https://wandbox.org/permlink/3m7i49Zzs3fdikVX). –

+1

@KerrekSB 경고 문구없이 위험 할 수 있습니다. 나는'thread t (std :: move (thread_r)); 또는'std :: thread t (run {});을 선호 할 것입니다. – sehe

+0

@sehe 그래서 이동이 선호됩니까? functor가 리소스 (예 : 열린 파일)를 포함하고 있고 그것을 이동하고'~ run()'을 호출하는 것이 문제가있는 경우, 맞습니까? deconstructor를 호출하지 않고 리소스를 정렬 할 수있는 방법이 있습니까? – qweruiop

답변

5

규칙 :

#include <iostream> 
#include <thread> 

using namespace std; 

class run { 
public: 
    run() { cout << "in run()" << endl; } 
    ~run() { cout << "in ~run()" << endl; } 
    void operator()() {}; 
}; 

int main() { 
    run thread_r; 
    thread t(thread_r); 
    t.join(); 
} 

출력을 준다.

소멸자를 정의하면 컴파일러는 여전히 기본 복사 생성자 및 할당 연산자를 생성합니다.

불행히도 소멸자를 정의한 경우 리소스 할당 해제와 관련하여 특별한 처리가 필요하므로 기본 복사 및 할당 코드가 잘못 될 수 있습니다.

좋은 사례 인은 삭제하더라도 적어도 생성자와 할당 연산자를 제공하기 위해 항상 "변하지 않습니다."라는 의미입니다.

당신이 이것을 제공한다면 올바른 이동 연산자도 쓸 수 있습니다.

#include <iostream> 
#include <thread> 

using namespace std; 

class run { 
public: 
    run() { cout << "in run()" << endl; } 
    run(const run&) { cout << "copied()" << endl; } 
    run(run&&) { cout << "moved()" << endl; } 
    run& operator=(const run&) { cout << "copy-assigned()" << endl; return *this; } 
    run& operator=(run &&) { cout << "move-assigned()" << endl; return *this; } 
    ~run() { cout << "in ~run()" << endl; } 
    void operator()() {}; 
}; 

int main() { 
    run thread_r; 
    thread t(thread_r); 
    t.join(); 
} 

예 출력 :

#include <iostream> 
#include <thread> 
#include <vector> 

using namespace std; 

class run { 
public: 
    run() 
    : lifetime("constructed") 
    { 
     cout << lifetime << endl; 
    } 

    run(const run& other) 
    : lifetime("copied from " + other.lifetime) 
    { 
     cout << lifetime << endl; 
    } 
    run(run&& other) 
    : lifetime("move-constructed from " + other.lifetime) 
    { 
     other.lifetime = "[zombie] - " + other.lifetime; 
     cout << lifetime << endl; 
    } 
    run& operator=(const run& other) 
    { 
     lifetime = "copy assigned from " + other.lifetime + ", was once " + lifetime; 
     cout << lifetime << endl; 
     return *this; 
    } 

    run& operator=(run &&other) 
    { 
     lifetime = "move-assigned from " + other.lifetime + ", was once " + lifetime; 
     other.lifetime = "[zombie] - " + other.lifetime; 
     cout << lifetime << endl; 
     return *this; 
    } 

    ~run() 
    { 
     lifetime = "lifetime ending: " + lifetime; 
     cout << lifetime << endl; 
    } 

    void operator()() {}; 

    std::string lifetime;  
}; 

int main() { 
    run thread_r; 
    thread t(thread_r); 
    t.join(); 
} 

샘플 출력 :

constructed 
copied from constructed 
move-constructed from copied from constructed 
lifetime ending: [zombie] - copied from constructed 
lifetime ending: move-constructed from copied from constructed 
lifetime ending: constructed 

여기를

in run() 
copied() 
moved() 
in ~run() 
in ~run() 
in ~run() 

는 생성자와 소멸자에서 무슨 일이 일어나고 있는지 설명 할 수있는 업데이트 버전입니다 술을 마시지 않는 사람 복사 생성자와 이동 생성자의 정확한 동작에 관해서는 명확해질 때까지 디버거에서이 코드를 사용하는 것이 좋습니다.

+0

모든 사람이 이해할 수 있도록 5/3/0의 규칙을 따르는 언어는 없습니다.하지만 그렇게해야하거나 모든 종류의 버그와 혼란을 겪을 위험이 있습니다. – Justin

+0

@Justin 페어 포인트. 내가 편집 할게. –

+0

나는 혼란 스럽네요. 당신의 예제에서 왜 원본과 사본 (두 개체)과 세 파괴가 있습니까? –