C++ 多线程中成员函数如何传参?拷贝、引用还是指针?

· 在 C++ 多线程编程中,很多初学者都会遇到一个问题:

成员函数怎么在线程中调用?对象要传指针还是引用?能不能直接传值?

本文通过一个小例子,带你从原理出发,彻底搞懂 std::thread 调用类成员函数时,传参的几种方式和区别。

示例类 A

我们先定义一个简单的类 A,包含一个成员变量和一个成员函数:

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

class A {
public:
    int x = 0;
    void foo() {
        x = 100;
        cout << "foo() called, x = " << x << endl;
    }
};

错误用法一:直接传对象

cpp 复制代码
A a;
std::thread t(&A::foo, a);  // ⚠️ 虽然能编译,但有坑
t.join();

解释:

  • std::thread 默认是 值传递

  • 你传的是 a,但实际上线程中收到的是 a 的副本

  • 所以你在线程里修改的是副本,不会影响主线程中的 a

示例输出:

cpp 复制代码
cout << a.x << endl;  // 输出 0,不是 100

你以为改了,其实没改!

正确方式一:传指针

cpp 复制代码
A a;
std::thread t(&A::foo, &a);  // 传对象地址
t.join();

原理:

  • &A::foo 是成员函数指针;

  • &a 是对象地址(A*);

  • 所以最终调用的就是 (a.*foo)(),真正作用于原对象。

使用智能指针也可以:

cpp 复制代码
auto a = std::make_shared<A>();
std::thread t(&A::foo, a);  // ✅ shared_ptr 也能用,自动转换为 A*

正确方式二:传引用(std::ref)

传引用而不是指针,标准库也支持

cpp 复制代码
A a;
std::thread t(&A::foo, std::ref(a));  // ✅ 强制引用传递
t.join();

错误用法二:对象不可拷贝

如果你的类禁止了拷贝构造:

cpp 复制代码
class A {
public:
    A() = default;
    A(const A&) = delete;  // ❌ 禁止拷贝
    void foo() {}
};

那么这行:

复制代码
std::thread t(&A::foo, a);  // ❌ 编译失败

会报错!因为 a 不能被拷贝进线程。

总结:传对象给线程的三种方式对比

写法 是否作用于原对象 是否安全 推荐情况
std::thread t(&A::foo, a); ❌ 否(拷贝副本) 对象可拷贝、修改无所谓
std::thread t(&A::foo, &a); ✅ 是 推荐,简单直接
std::thread t(&A::foo, std::ref(a)); ✅ 是 推荐,传引用更语义清晰
std::thread t(&A::foo, smartPtr); ✅ 是 推荐用于共享对象管理

补充:C++ 成员函数调用的本质到底是什么?为什么要传对象?

平常写代码这样调用成员函数:

复制代码
A a;
a.foo();

这句话到底发生了什么呢?它为什么能运行?

foo 是属于类 A 的函数,但是它不能单独运行,必须靠"某个具体的对象"来调用。

就好比:

  • foo 是"技能";

  • a 是"拥有这个技能的人";

只有 a 这个人,才可以施展 foo 这个技能。

语言层面怎么实现的?

C++ 编译器做了一件事:

它把成员函数 转换成带有隐藏参数的普通函数

这个隐藏参数叫 this,代表"哪个对象调用了这个函数"。

所以 a.foo(),其实等价于:

复制代码
foo(&a);

这里的 &a 就是 this 指针,告诉函数"你是代表谁去执行的"。

这样解释一下:

  • foo 本质就是:

    void foo(A* this)
    {
    // 函数体
    }

  • a.foo() 实际调用是:

    foo(&a); // 给函数传入a的地址

那多线程调用成员函数为什么要传对象?

当你写:

复制代码
std::thread t(&A::foo, a);

你是在告诉线程:

  • 我要调用类 A 的成员函数 foo

  • a 这个对象来调用它(传入 this 指针)

线程内部实际上是:

复制代码
(a.*&A::foo)();

也就是用 a 这个对象执行 foo

如果你写成:

复制代码
std::thread t(&A::foo);

这样不行!线程里没告诉它用谁调用函数,它不知道 this 是谁!

总结几个关键点:

你写的代码 编译器理解成什么 为什么要传对象
a.foo() foo(&a) 需要 this 指针,告诉函数"你代表谁执行"
std::thread t(&A::foo, a); 线程内部调用 (a.*&A::foo)() 线程里也需要 this 指针(对象),才能调用成员函数
std::thread t(&A::foo); 编译错误,因为没传 this 指针 缺少对象,没法调用成员函数

打个简单比喻

假如成员函数是电话指令 ,对象是电话机

  • 你要给电话机发送指令(成员函数调用),必须先知道是哪部电话机(对象);

  • 你不能直接说"拨号",却不给电话机(对象);

  • this 就是电话机的地址;

  • 线程调用成员函数,也必须知道是哪部电话机,才能执行指令。

所以,为什么 std::thread 调用成员函数时:

  • 第一个参数是成员函数指针("电话指令");

  • 第二个参数必须是对象或者对象指针("电话机");

额外说明

  • 静态成员函数 没有 this,它更像普通函数,不需要对象,线程里可以直接调用;

  • 传对象时是传"指针"或"引用",因为成员函数需要用 this 指向那个对象。

相关推荐
Lhan.zzZ3 小时前
笔记_2026.4.28_004
c++·ide·笔记·qt
MATLAB代码顾问3 小时前
5大智能算法优化标准测试函数对比(Python实现)
开发语言·python
wuminyu4 小时前
专家视角看Java字节码加载与存储指令机制
java·linux·c语言·jvm·c++
万粉变现经纪人5 小时前
如何解决 pip install llama-cpp-python 报错 未安装 CMake/Ninja 或 CPU 不支持 AVX 问题
开发语言·python·开源·aigc·pip·ai写作·llama
清风明月一壶酒5 小时前
OpenClaw自动处理Word文档全流程
开发语言·c#·word
其实防守也摸鱼5 小时前
CTF密码学综合教学指南--第五章
开发语言·网络·笔记·python·安全·网络安全·密码学
木喃的井盖5 小时前
无锁队列细节
c++·工程
王老师青少年编程5 小时前
csp信奥赛C++高频考点专项训练之字符串 --【字符串基础】:输出亲朋字符串
c++·字符串·csp·高频考点·信奥赛·专项训练·输出亲朋字符串
WBluuue6 小时前
数据结构与算法:莫队(一):普通莫队与带修莫队
c++·算法
小郑加油6 小时前
python学习Day12:pandas安装与实际运用
开发语言·python·学习