传递临时对象要注意的陷阱
- 引用虽然编译器进行了值拷贝,但是不推荐用。
- 指针一定会出现临时对象的传递错误
void myfunc_detach(const int& i, const char* ptr)
{
cout << i << endl; //调式是myvar 和 i 是两个不同的地址,即编译器进行了值拷贝操作
cout << ptr << endl; //str 和 ptr 是同一个地址,假如detach后主线程先结束,临时对象str会被销毁,ptr指向非法地址空间
}
int main()
{
int i1 = 12;
int& myvar = i1;
char str[] = { "this is a string!" };
thread mythr(myfunc_detach, myvar, str);
//mythr.join();
mythr.detach(); //这里会出现非预期的值传递异常
cout << "hi~" << endl;
}
关于指针传输出现得问题,可以暂时修改形参的方式更正这个隐藏的陷阱
void myfunc_detach(const int& i, const string& ptr)
{
...
}
但是这里存在的问题是:可能main线程已经结束了,str
才会去进行string
类型转换,这也是对非法内存空间的非法操作。
- 真正正确的更正如下:
...
thread mythr(myfunc_detach, myvar, string(str));
...
string(str)
会生成临时对象,而临时对象会绑定到const string& ptr
这个引用上。
临时对象传递给线程是可行的,此时即使.detach()
子线程也是可以安全运行的。
- 类比说明
using namespace std;
class Atrmp
{
public:
int var;
Atrmp(int a);
Atrmp(const Atrmp& cla);
~Atrmp();
private:
};
Atrmp::Atrmp(int a):var(a)
{
cout << "Atrmp::Atrmp(int a)构造函数执行" << this << endl;
}
Atrmp::Atrmp(const Atrmp& cla):var(cla.var) {
cout << "Atrmp::Atrmp(Atrmp& cla):var(cla.var)拷贝构造函数执行" << this << endl;
}
Atrmp::~Atrmp()
{
cout << "Atrmp::~Atrmp()析构函数执行" << this << endl;
}
void func(int a, const Atrmp& rmp)
{
cout << &rmp << endl;
}
int main()
{
int x = 1;
int trmp = 12;
//thread my1(func, x, trmp);
thread my1(func, x, Atrmp(trmp));
//my1.join();
my1.detach();
return 0;
}
thread my1(func, x, trmp);
my1.join();
- join()运行的结果
thread my1(func, x, Atrmp(trmp));
my1.detach();
- detach()时运行的结果
detach
时注意事项
- 若传递
int
这种简单的类型参数,建议使用值传递,不要用引用。 - 如果传递类对象,避免隐式类型转换,全部在创建线程那一行构建出临时对象出来,在线程函数那里用引用来接,否则系统会额外构建一个对象出来。
线程ID的获取
std::this_thread::get_id()
传递类对象,智能指针作为线程参数
std::ref
在传递对象的时候,实参加上该修饰,就可以在子线程中修改值了。此时的参数直接传递,并没有进行拷贝
智能指针的传递示意如下:
void func2(unique_ptr <int> p) {
}
int main(){
unique_ptr<int> mp(new int(10));
thread my2(func2, std::move(mp));
my2.join();
return 0;
}
用成员函数指针作线程参数
using namespace std;
class Atrmp
{
public:
int var;
Atrmp(int a);
Atrmp(const Atrmp& cla);
~Atrmp();
void classfunc(int num);
private:
};
Atrmp::Atrmp(int a):var(a)
{
cout << "Atrmp::Atrmp(int a)构造函数执行" << this << "Thread_ID:" << this_thread::get_id() << endl;
}
Atrmp::Atrmp(const Atrmp& cla):var(cla.var) {
cout << "Atrmp::Atrmp(Atrmp& cla):var(cla.var)拷贝构造函数执行" << this << "Thread_ID:" << this_thread::get_id() << endl;
}
Atrmp::~Atrmp()
{
cout << "Atrmp::~Atrmp()析构函数执行" << this << "Thread_ID:" << this_thread::get_id() << endl;
}
void Atrmp::classfunc(int num)
{
cout << "classfunc执行了" << this << "Thread_ID:" << this_thread::get_id() << endl;
}
int main()
{
Atrmp myobj(10);
thread myth(&Atrmp::classfunc, myobj, 12);
//thread myth(&Atrmp::classfunc, ref(myobj), 12);
myth.join();
return 0;
}
可调用类对象最为线程指针
using namespace std;
class mycla1
{
public:
mycla1(int a);
mycla1(const mycla1& c);
~mycla1();
void operator()(int x);
int m_i;
private:
};
mycla1::mycla1(int a):m_i(a)
{
cout << "mycla1::mycla1(int a)构造函数执行" << this << "Thread_ID:" << this_thread::get_id() << endl;
}
mycla1::mycla1(const mycla1& c) : m_i(c.m_i)
{
cout << "mycla1::mycla1(const mycla1& c)拷贝构造函数执行" << this << "Thread_ID:" << this_thread::get_id() << endl;
}
mycla1::~mycla1()
{
cout << "mycla1::~mycla1()析构函数执行" << this << "Thread_ID:" << this_thread::get_id() << endl;
}
void mycla1::operator()(int x)
{
cout << "mycla1 mycla1::operator()(int x)函数执行" << this << "Thread_ID:" << this_thread::get_id() << endl;
//return *this;
}
int main()
{
mycla1 myobjective(7);
thread mytive(myobjective, 12);
mytive.join();
return 0;
}