使用成员函数启动std::thread时应传对象、指针还是引用?
std::thread绑定类非静态成员函数的传参规范
首先明确核心规则:std::thread构造函数对传入的可调用对象和参数遵循标准INVOKE语义,且默认会将所有传入参数按值拷贝/移动到线程内部独立存储,再在线程上下文完成调用。对于非静态成员函数指针,跟在其后的第一个参数只要满足“可被解引用为对应类实例”的要求就能通过编译,你提到的三种写法语法上都合法,但行为差异极大,适用场景完全不同,不存在绝对的唯一正确写法,只需要根据需求选对应行为的传参方式即可。
三种写法的具体行为和注意事项:
- 直接传入对象实例(
my_x)
线程会对传入的对象做一次拷贝,后续成员函数调用是在这份独立的副本上执行。
优势是生命周期安全:哪怕原对象在线程运行期间被销毁、被修改,都不会影响线程内的副本,不会出现悬垂访问问题。
缺陷也很明确:如果类不可拷贝会直接编译失败;大对象拷贝会有额外性能开销;如果你的需求是让线程修改原对象的状态,这种写法只会修改副本,会出现逻辑错误。 - 传入对象地址(
&my_x)
线程只会拷贝传入的裸指针,后续直接对指针指向的原对象调用成员函数,不会产生对象副本。
优势是无额外拷贝开销,操作的是原对象。
风险极高:你必须手动保证指针指向的对象在线程整个运行周期内都保持有效、不会被析构、不会被移动内存位置,一旦对象提前销毁,线程访问就会触发野指针的未定义行为,轻则崩溃重则出现随机逻辑bug。这种写法属于C++11早期的习惯遗留,语义模糊,新代码不推荐使用。 - 传入
std::ref(my_x)包装的引用
线程只会拷贝轻量的std::reference_wrapper实例,调用时自动解引用为原对象的引用,成员函数直接操作原对象,无额外对象拷贝开销。
优势是语义清晰:明确表达“传递引用、不拷贝对象、操作原实例”的意图,和std::thread传递普通函数引用参数的规则完全统一(普通函数参数要求传引用时,必须用std::ref包装才能正确传递,否则编译失败),代码可读性更好。
风险和传裸指针完全一致:必须保证原对象生命周期覆盖线程的全运行周期,否则会出现悬垂引用的未定义行为。
选型建议
- 不需要修改原对象状态、类可拷贝且拷贝成本可接受时,优先选直接传对象实例,生命周期安全无额外心智负担
- 需要操作原对象、且能严格保证原对象生命周期长于线程运行时间时,优先选
std::ref包装传参,语义最规范- 非必要不要用传裸指针的写法,功能和
std::ref完全等价但语义模糊,容易给后续维护者造成困惑
补充提醒:你给出的示例代码中重复声明了三个同名std::thread t变量,实际编译会报错,测试时需要给每个线程对象单独命名,分别执行join()。
内容的提问来源于stack exchange,提问作者Kevin eyeson
相关产品推荐
相关产品推荐

