主要分为3种情况
- 普通函数
- 类的成员函数
- 参数为智能指针
需要特别注意,线程具有内部存储空间,参数会按默认方式复制为临时变量,然后再将该临时变量作为右值传给新线程上的函数或可调用对象
普通函数传参#include#include #include using namespace std; // void myPrint(string& t_) // void myPrint(const string &t_) void myPrint(string&& t_) { t_ = "Ouch!"; cout << "t_ location:" << &t_ << endl; cout << "myPrint:" << t_ << endl; } int main() { char buff[]="Watch out!"; string text = "Hello C++!"; cout << "text location:" << &text << endl; cout << "buff location:" << &buff << endl; cout << "更改前text:" << text << endl; // thread t(myPrint, ref(text)); thread t(myPrint, text); t.join(); // t.detach(); cout << "更改后text:" << text << endl; cout << "main end!" << endl; return 0; }
注意点:
1、因为是以右值传递给函数,因此下列普通的引用是不可行的,编译不会通过
void myPrint(string& t_)
因此只能用另外两种引用方式
2、参数在传递前已经进行了一次拷贝,因此引用取的是临时变量的地址,函数里的更改并不会影响实参,运行上面的程序,输出
text location:0x7ffd5d2f84d0 buff location:0x7ffd5d2f84fd 更改前text:Hello C++! t_ location:0x55dbf3e3e288 myPrint:Ouch! 更改后text:Hello C++! main end!
发现地址不同且text的内容并没有更改,因此可以理解为假引用
如果需要以真引用的方式传递参数,则要用std::ref( )函数加以包装,这与std::bind( )函数类似,如下:
thread t(myPrint, ref(text));
运行后输出
text location:0x7ffcaaa3e6f0 buff location:0x7ffcaaa3e71d 更改前text:Hello C++! t_ location:0x7ffcaaa3e6f0 myPrint:Ouch! 更改后text:Ouch! main end!
注意此时的函数声明为
void myPrint(string& t_)
此时是我们正常理解的左值引用
类的成员函数传参又可以分为两种情况,由该成员函数是否为静态成员函数为依据
1、静态成员函数
#include#include #include using namespace std; class A { public: A(int a):m_a(a) { cout << "A 构造函数 。。。" << endl; } A(const A& a):m_a(a.m_a) { cout << "A 拷贝构造函数 。。。" << endl; } void operator()(){} static void printtext(string& t_) // void printtext(const string& t_) { t_ = "Ouch!"; cout << "t_ location:" << &t_ << endl; cout << "t_:" << t_ << endl; } ~A(){cout << "A 析构函数 。。。" << endl;} public: int m_a; }; int main() { char buff[]="Watch out!"; string text = "Hello C++!"; cout << "text location:" << &text << endl; cout << "buff location:" << &buff << endl; cout << "更改前text:" << text << endl; A A1(5); // thread t(A1); // thread t(&A::printtext,&A1,text); thread t(&A::printtext,ref(text)); t.join(); // t.detach(); cout << "更改后text:" << text << endl; cout << "main end!" << endl; return 0; }
因为静态成员函数在内存中位于全局区,属于类而不属于类的对象,因此注明该函数在该类的作用域下即可
thread t(&A::printtext,ref(text));
此时取地址符没有影响,下面也是可行的
thread t(A::printtext,ref(text));
输出如下:
text location:0x7ffdda801e90 buff location:0x7ffdda801ebd 更改前text:Hello C++! t_ location:0x7ffdda801e90 t_:Ouch! 更改后text:Ouch! main end!
2、非静态成员函数
此时若要将某个类的成员函数设定为线程函数,则应传入一个函数指针指向该成员函数,此外还要给出对象指针,如下:
thread t(&A::printtext,&A1,text);
有一个非常值得注意的地方,有两个取地址符,若去掉第一个,报错
/home/prejudice/Cplus_learning/src/thread_02.cpp:54:17: error: invalid use of non-static member function ‘void A::printtext(std::__cxx11::string&&)’
thread t(A::printtext,&A1,text);
因此第一个取地址符必须保留,那么看第二个
若去掉第二个参数的&,运行输出如下:
text location:0x7ffd84776370 buff location:0x7ffd8477639d 更改前text:Hello C++! A 构造函数 。。。 A 拷贝构造函数 。。。 A 拷贝构造函数 。。。 A 析构函数 。。。 t_ location:0x559be66f7288 t_:Ouch! A 析构函数 。。。 更改后text:Hello C++! main end! A 析构函数 。。。
为什么会有三次构造和析构呢,我是这样理解的
text location:0x7ffd84776370 buff location:0x7ffd8477639d 更改前text:Hello C++! A 构造函数 。。。 //main中A1的构造 A 拷贝构造函数 。。。 //拷贝构造一个临时对象 A 拷贝构造函数 。。。 //将该临时对象拷贝到printtext函数中,作为该函数内的局部变量 A 析构函数 。。。 //临时对象使用完毕立即释放 t_ location:0x559be66f7288 t_:Ouch! A 析构函数 。。。 //printtext函数运行结束释放栈区上的数据 更改后text:Hello C++! main end! A 析构函数 。。。 //mian结束调用A1的析构
如果保留第二个参数的&运行输出如下
text location:0x7ffe6078fd40 buff location:0x7ffe6078fd6d 更改前text:Hello C++! A 构造函数 。。。 t_ location:0x55af5cc36288 t_:Ouch! 更改后text:Hello C++! main end! A 析构函数 。。。
可以看到只在主函数中构造了一次,而免去了两次拷贝,提高了运行效率
因此建议同时传入函数指针和对象指针
这里同样要注意参数的真假引用问题
参数为智能指针智能指针有三种:
- 独占指针——unique_ptr
- 共享指针——shared_ptr
- 弱指针——weak_ptr
这里只讲独占指针和共享指针做参数,因为弱指针在目前读过的代码里见得实在很少
大家可以先补一下知识,在看下面的内容
http://c.biancheng.net/view/1478.html
unique_ptr为参数
#include#include #include #include using namespace std; // void myPrint(string& t_) // void myPrint(const string &t_) // void myPrint(string&& t_) // void myPrint(unique_ptr t_) void myPrint(shared_ptr t_) { *t_ = "Ouch!"; cout << "t_ location:" << &t_ << endl; cout << "myPrint:" << *t_ << endl; } int main() { // string text = "Hello C++!"; // unique_ptr text(new string); unique_ptr text = make_unique ("Hello C++!"); // shared_ptr text = make_shared ("Hello C++!"); cout << "text location:" << &text << endl; cout << "更改前text:" << *text << endl; thread t(myPrint,move(text)); t.join(); // t.detach(); // cout << "更改后text:" << *text << endl; cout << "main end!" << endl; return 0; }
运行输出:
text location:0x7ffd55a3c1e8 更改前text:Hello C++! t_ location:0x7f8bb48e8dc0 myPrint:Ouch! main end!
注意点:
1、利用move函数向线程转移动态对象的归属权,转移后text变为NULL指针,因此要注释
// cout << "更改后text:" << *text << endl;
否则运行后会提示段错误,核心已转储
2、make_unique是C++14标准里的,因此要更改CMakeLists.txt
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -pthread")
否则编译时会报错
error: ‘make_unique’ was not declared in this scope
unique_ptr text = make_unique();
shared_ptr为参数
#include#include #include #include using namespace std; // void myPrint(string& t_) // void myPrint(const string &t_) // void myPrint(string&& t_) // void myPrint(unique_ptr t_) void myPrint(shared_ptr t_) { *t_ = "Ouch!"; cout << "t_ location:" << &t_ << endl; cout << "myPrint:" << *t_ << endl; } int main() { // string text = "Hello C++!"; // unique_ptr text(new string); // unique_ptr text = make_unique ("Hello C++!"); shared_ptr text = make_shared ("Hello C++!"); cout << "text location:" << &text << endl; cout << "更改前text:" << *text << endl; thread t(myPrint, text); // thread t(myPrint,move(text)); t.join(); // t.detach(); cout << "更改后text:" << *text << endl; cout << "main end!" << endl; return 0; }
因为shared_ptr指针都指向同一块地址,故在main中text仍能打印输出
text location:0x7fffc22c9410 更改前text:Hello C++! t_ location:0x7fd464a9bdc0 myPrint:Ouch! 更改后text:Ouch! main end!
同样用类的成员函数进行测试
#include#include #include #include using namespace std; class A { public: A(int a) :m_a(a) { cout << "A 构造函数 。。。" << endl; } A(const A& a) :m_a(a.m_a) { cout << "A 拷贝构造函数 。。。" << endl; } void operator()() {} //void printtext(string& t_) // void printtext(const string& t_) void printtext(shared_ptr t_) { *t_ = "Ouch!"; cout << "t_ location:" << &t_ << endl; cout << "t_:" << *t_ << endl; } ~A() { cout << "A 析构函数 。。。" << endl; } public: int m_a; }; int main() { // string text = "Hello C++!"; // unique_ptr text(new string); // unique_ptr text = make_unique ("Hello C++!"); shared_ptr text = make_shared ("Hello C++!"); cout << "text location:" << &text << endl; cout << "更改前text:" << *text << endl; A A1(5); thread t(&A::printtext, &A1, text); t.join(); cout << "更改后text:" << *text << endl; cout << "main end!" << endl; return 0; }
打印输出:
text location:0098FC38 更改前text:Hello C++! A 构造函数 。。。 t_ location:00AFFCC4 t_:Ouch! 更改后text:Ouch! main end! A 析构函数 。。。
注意到无论是普通函数还是成员函数t_ location与text location都不相同,最后text居然还能成功修改,不得不感叹C++的强大
至于原因可能涉及到比较底层的逻辑,大家有兴趣可以自己学习了解下



