Brakuje instrumentalnej konstrukcji kopii i konstrukcji przenoszenia. Prosta modyfikacja programu dostarczy dowodów na to, gdzie mają miejsce konstrukcje.
Kopiuj konstruktora
#include <iostream>
#include <thread>
#include <functional>
using namespace std;
class tFunc{
int x;
public:
tFunc(){
cout<<"Constructed : "<<this<<endl;
x = 1;
}
tFunc(tFunc const& obj) : x(obj.x)
{
cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
}
~tFunc(){
cout<<"Destroyed : "<<this<<endl;
}
void operator()(){
x += 10;
cout<<"Thread running at : "<<x<<endl;
}
int getX() const { return x; }
};
int main()
{
tFunc t;
thread t1{t};
if(t1.joinable())
{
cout<<"Thread is joining..."<<endl;
t1.join();
}
cout<<"x : "<<t.getX()<<endl;
return 0;
}
Wyjście (adresy się różnią)
Constructed : 0x104055020
Copy constructed : 0x104055160 (source=0x104055020)
Copy constructed : 0x602000008a38 (source=0x104055160)
Destroyed : 0x104055160
Thread running at : 11
Destroyed : 0x602000008a38
Thread is joining...
x : 1
Destroyed : 0x104055020
Kopiuj konstruktora i przenieś konstruktora
Jeśli podasz ctor ruchu, będzie on preferowany dla co najmniej jednego z tych kopii:
#include <iostream>
#include <thread>
#include <functional>
using namespace std;
class tFunc{
int x;
public:
tFunc(){
cout<<"Constructed : "<<this<<endl;
x = 1;
}
tFunc(tFunc const& obj) : x(obj.x)
{
cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
}
tFunc(tFunc&& obj) : x(obj.x)
{
cout<<"Move constructed : "<<this<< " (source=" << &obj << ')' << endl;
obj.x = 0;
}
~tFunc(){
cout<<"Destroyed : "<<this<<endl;
}
void operator()(){
x += 10;
cout<<"Thread running at : "<<x<<endl;
}
int getX() const { return x; }
};
int main()
{
tFunc t;
thread t1{t};
if(t1.joinable())
{
cout<<"Thread is joining..."<<endl;
t1.join();
}
cout<<"x : "<<t.getX()<<endl;
return 0;
}
Wyjście (adresy się różnią)
Constructed : 0x104057020
Copy constructed : 0x104057160 (source=0x104057020)
Move constructed : 0x602000008a38 (source=0x104057160)
Destroyed : 0x104057160
Thread running at : 11
Destroyed : 0x602000008a38
Thread is joining...
x : 1
Destroyed : 0x104057020
Referencje zapakowane
Jeśli chcesz ominąć te kopie, możesz owinąć swój program wywołujący w wrapper referencyjny ( std::ref
). Ponieważ chcesz wykorzystać t
po zakończeniu gwintowania, jest to opłacalne w twojej sytuacji. W praktyce należy zachować szczególną ostrożność podczas łączenia wątków z odwołaniami do obiektów wywołujących, ponieważ czas życia obiektu musi trwać co najmniej tak długo, jak wątek wykorzystujący odwołanie.
#include <iostream>
#include <thread>
#include <functional>
using namespace std;
class tFunc{
int x;
public:
tFunc(){
cout<<"Constructed : "<<this<<endl;
x = 1;
}
tFunc(tFunc const& obj) : x(obj.x)
{
cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
}
tFunc(tFunc&& obj) : x(obj.x)
{
cout<<"Move constructed : "<<this<< " (source=" << &obj << ')' << endl;
obj.x = 0;
}
~tFunc(){
cout<<"Destroyed : "<<this<<endl;
}
void operator()(){
x += 10;
cout<<"Thread running at : "<<x<<endl;
}
int getX() const { return x; }
};
int main()
{
tFunc t;
thread t1{std::ref(t)}; // LOOK HERE
if(t1.joinable())
{
cout<<"Thread is joining..."<<endl;
t1.join();
}
cout<<"x : "<<t.getX()<<endl;
return 0;
}
Wyjście (adresy się różnią)
Constructed : 0x104057020
Thread is joining...
Thread running at : 11
x : 11
Destroyed : 0x104057020
Zauważ, że chociaż zachowałem przeciążenia copy-ctor i move-ctor, żadne z nich nie zostało wywołane, ponieważ opakowanie referencyjne jest teraz przedmiotem kopiowania / przenoszenia; nie rzecz, do której się odnosi. Ponadto to końcowe podejście zapewnia to, czego prawdopodobnie szukałeś; t.x
z powrotem main
jest w rzeczywistości zmodyfikowany do 11
. Nie było w poprzednich próbach. Nie mogę tego jednak wystarczająco podkreślić: bądź ostrożny . Żywotność obiektu ma kluczowe znaczenie .
Rusz się i nic więcej
Wreszcie, jeśli nie jesteś zainteresowany zachowaniem, t
tak jak w przykładzie, możesz użyć semantyki move, aby wysłać instancję bezpośrednio do wątku, poruszając się po drodze.
#include <iostream>
#include <thread>
#include <functional>
using namespace std;
class tFunc{
int x;
public:
tFunc(){
cout<<"Constructed : "<<this<<endl;
x = 1;
}
tFunc(tFunc const& obj) : x(obj.x)
{
cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
}
tFunc(tFunc&& obj) : x(obj.x)
{
cout<<"Move constructed : "<<this<< " (source=" << &obj << ')' << endl;
obj.x = 0;
}
~tFunc(){
cout<<"Destroyed : "<<this<<endl;
}
void operator()(){
x += 10;
cout<<"Thread running at : "<<x<<endl;
}
int getX() const { return x; }
};
int main()
{
thread t1{tFunc()}; // LOOK HERE
if(t1.joinable())
{
cout<<"Thread is joining..."<<endl;
t1.join();
}
return 0;
}
Wyjście (adresy się różnią)
Constructed : 0x104055040
Move constructed : 0x104055160 (source=0x104055040)
Move constructed : 0x602000008a38 (source=0x104055160)
Destroyed : 0x104055160
Destroyed : 0x104055040
Thread is joining...
Thread running at : 11
Destroyed : 0x602000008a38
Tutaj możesz zobaczyć, że obiekt jest tworzony, odwołanie do wartości tego samego - a następnie wysyłane prosto do std::thread::thread()
, gdzie jest ponownie przenoszone do ostatecznego miejsca spoczynku, którego właścicielem jest wątek od tego momentu. Zaangażowani są nie kopiownicy. Rzeczywiste kropki opierają się o dwie skorupy i konkretny obiekt docelowy.