1 /* 2 * 实例场景: 3 * 2. std::thread 构造函数的第一个参数为成员函数时,参数的传递。 4 * 传递方式:std::thread第一个参数为成员函数的函数指针, std::thread 从第二个开始的参数,依次传递给成员函数的参数表 5 * Obj obj; 6 * std::thread t(&Obj::func, &obj, param1); 7 * std::thread t(&Obj::func, &obj, param1, param2, ..., paramn); 8 * 9 * * * 分别就参数的几种常见形式进行讨论: 10 * 11 * Object obj; 12 * Param param(10); 13 * 14 * a) 参数为值类型, 例如 Param。 func形参地址与实参地址不同,发生了: 拷贝构造+移动构造。 15 * std::thread t(&Object::func, &obj, param); 16 * 17 * b) 参数为引用类型, 例如 Param &。 func形参地址与实参地址分两种方式而不同 !!!请注意:默认变量类型和自定义类类型的情况并不一样!!! 18 * b-1) 默认方式 19 * std::thread t(&Object::func, &obj, param); 形参地址与实参地址不同, 发生了:拷贝构造。 20 * 21 * b-2) std::ref() 方式 22 * std::thread t(&Object::func, &obj, std::ref(param)); 形参地址与实参地址相同, 未发生拷贝构造,未发生移动构造 23 * 24 * c) 参数为指针类型, 例如 int *。 func形参地址与实参地址相同,未发生拷贝。 25 * std::thread t(&Object::func, &obj, ¶m); 26 */
* 问-思考:
* 2. std::thread 构造函数执行时,不同的参数类型在传递时,都发生了什么事情?比如:值类型,引用类型,指针类型
1 //! [2] ====================== *实例场景-2=====+=====问-思考-2 *================== 2 template<typename T> 3 class luckyBear{ 4 public: 5 luckyBear(T t):m_t(t){ 6 std::cout<<"luckyBear<"<<_typeName()<<"> 构造函数执行..."<<std::endl; 7 } 8 luckyBear(luckyBear & other):m_t(other.m_t){ 9 std::cout<<"luckyBear<"<<_typeName()<<"> 拷贝构造函数执行..."<<std::endl; 10 } 11 luckyBear(luckyBear &&other){ 12 m_t = std::move(other.m_t); 13 std::cout<<"luckyBear<"<<_typeName()<<"> 移动构造函数执行..."<<std::endl; 14 } 15 ~luckyBear(){ 16 std::cout<<"luckyBear<"<<_typeName()<<"> 析构函数执行..."<<std::endl; 17 } 18 19 void operator=(luckyBear &other){ 20 m_t = other.m_t; 21 std::cout<<"luckyBear<"<<_typeName()<<"> 赋值运算符函数执行..."<<std::endl; 22 } 23 24 void operator=(luckyBear &&other){ 25 m_t = std::move(other.m_t); 26 std::cout<<"luckyBear<"<<_typeName()<<"> 移动赋值运算符函数执行..."<<std::endl; 27 } 28 29 T value(){ 30 return m_t; 31 } 32 33 private: 34 std::string _typeName(){ 35 const std::type_info & _type = typeid(T); 36 return _type.name(); 37 } 38 39 private: 40 T m_t; 41 }; 42 43 44 template<typename T> 45 class thread_test : public luckyBear<T>{ 46 47 public: 48 thread_test():luckyBear<T>(0){} 49 }; 50 51 typedef luckyBear<int> VType; //Value type 52 typedef luckyBear<int>& RType; //Reference type 53 typedef luckyBear<int>* PType; //Pointer type 54 55 class thread_construct{ 56 57 public: 58 void func1(VType v){ 59 std::cout<<v.value()<<std::endl; 60 } 61 62 void func2(RType rv){ 63 std::cout<<rv.value()<<std::endl; 64 } 65 66 void func3(PType pv){ 67 std::cout<<pv->value()<<std::endl; 68 } 69 };
1 //! [2-1] 值类型传递: (1次)拷贝构造 + (1次)移动构造 2 3 //分析 4 /* 5 1-从局部变量到线程的独立空间中,发生了拷贝构造; 6 2-从线程的独立空间到线程入口函数,发生了移动构造; 7 3-形参和实参地址不同; 8 */ 9 int main(int argc, char *argv[]){ 10 11 VType vt(10); 12 13 thread_construct _c; 14 std::thread _t1(&thread_construct::func1, &_c, vt); 15 16 _t1.join(); 17 std::cout<<"...lucky bear..."<<std::endl; 18 return 0; 19 20 } 21 //打印输出: 22 /* 23 luckyBear<int> 构造函数执行... 24 luckyBear<int> 拷贝构造函数执行... 25 luckyBear<int> 移动构造函数执行... 26 10 27 luckyBear<int> 析构函数执行... 28 luckyBear<int> 析构函数执行... 29 ...lucky bear... 30 luckyBear<int> 析构函数执行... 31 */ 32 //! [2-1]
1 //! [2-2-1] 引用类型传递--默认方式: (1次)拷贝构造 2 3 //分析 4 /* 5 1. 从局部变量到线程的独立空间中,发生了拷贝构造, “默认参数要拷贝到线程独立内存中,即使参数是引用的形式”; 6 2. 从线程的独立空间到线程入口函数,属于常规的给引用参数传值, 引用即别名,不开辟新空间; 7 3. 形参和实参地址不同; 8 */ 9 int main(int argc, char *argv[]){ 10 11 VType vt(10); 12 13 thread_construct _c; 14 std::thread _t1(&thread_construct::func2, &_c, vt); //默认方式 15 16 _t1.join(); 17 std::cout<<"...lucky bear..."<<std::endl; 18 return 0; 19 } 20 //打印输出: 21 /* 22 luckyBear<int> 构造函数执行... 23 luckyBear<int> 拷贝构造函数执行... 24 10 25 luckyBear<int> 析构函数执行... 26 ...lucky bear... 27 luckyBear<int> 析构函数执行... 28 */ 29 //! [2-2-1]
1 //! [2-2-2] 引用类型传递--std::ref()方式: 不发生拷贝构造,不发生移动构造 2 3 //分析 4 /* 5 1. 从局部变量到线程的独立空间中,属于常规的给引用参数传值, 引用即别名,不开辟新空间; 6 2. 从线程的独立空间到线程入口函数,属于常规的给引用参数传值, 引用即别名,不开辟新空间; 7 3. 形参和实参地址相同; 8 4. std::ref() 方式传递引用类型参数,可以做到线程内对参数的修改会作用到线程外部的实参变量,反过来也是一样。 9 */ 10 int main(int argc, char *argv[]){ 11 12 VType vt(10); 13 14 thread_construct _c; 15 std::thread _t1(&thread_construct::func2, &_c, std::ref(vt)); //std::ref() 方式 16 17 _t1.join(); 18 std::cout<<"...lucky bear..."<<std::endl; 19 return 0; 20 } 21 //打印输出: 22 /* 23 luckyBear<int> 构造函数执行... 24 10 25 ...lucky bear... 26 luckyBear<int> 析构函数执行... 27 */ 28 //! [2-2-2]
1 //! [2-3-1] 指针类型传递--默认方式: 不发生拷贝构造,不发生移动构造 2 3 //分析 4 /* 5 1. 从局部变量到线程的独立空间中,属于常规的给指针参数传值, 多个指针指向同一地址空间,不开辟新空间; 6 2. 从线程的独立空间到线程入口函数,属于常规的给指针参数传值, 多个指针指向同一地址空间,不开辟新空间; 7 3. 形参和实参地址相同; 8 4. 默认方式方式传递指针类型参数,可以做到线程内对参数的修改会作用到线程外部的实参变量,反过来也是一样。 9 */ 10 int main(int argc, char *argv[]){ 11 12 VType vt(10); 13 14 thread_construct _c; 15 std::thread _t1(&thread_construct::func3, &_c, &vt); //默认方式 16 17 _t1.join(); 18 std::cout<<"...lucky bear..."<<std::endl; 19 return 0; 20 } 21 //打印输出: 22 /* 23 luckyBear<int> 构造函数执行... 24 10 25 ...lucky bear... 26 luckyBear<int> 析构函数执行... 27 */ 28 //! [2-3-1]
1 //! [2-3-2] 指针类型传递--std::ref()方式: 不可以这样使用,编译报错 2 3 //分析 4 /* 5 */ 6 int main(int argc, char *argv[]){ 7 8 VType vt(10); 9 10 thread_construct _c; 11 12 //error info: C2893:未能使函数模板(unkown-type std::invoke(_Callable &&, _Types &&...)专用化 13 //std::thread _t1(&thread_construct::func3, &_c,std::ref(vt)); //std::ref() 方式 14 15 //_t1.join(); 16 std::cout<<"...lucky bear..."<<std::endl; 17 return 0; 18 } 19 //打印输出: 20 /* 21 */ 22 //! [2-3-2] 23 //! [2]
std::get<C++11多线程库>(07):向线程函数传递参数(4)
原创文章, 转载请注明出处!