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 指向那个对象。

相关推荐
Dxy12393102162 小时前
Python观察者模式详解:从理论到实战
开发语言·python·观察者模式
不写八个4 小时前
GoLang教程005:switch分支
开发语言·后端·golang
ZeroToOneDev5 小时前
Java(LinkedList和ArrayList底层分析)
java·开发语言
吃饭只吃七分饱5 小时前
C++第8章:IO库
开发语言·c++
宴之敖者、6 小时前
数组——初识数据结构
c语言·开发语言·数据结构·算法
青小莫6 小时前
c语言-数据结构-二叉树OJ
c语言·开发语言·数据结构·二叉树·力扣
典学长编程6 小时前
Java从入门到精通!第十一天(Java常见的数据结构)
java·开发语言·数据结构
后端小张6 小时前
智谱AI图生视频:从批处理到多线程优化
开发语言·人工智能·ai·langchain·音视频
m0dw6 小时前
js迭代器
开发语言·前端·javascript
Bonnie_12157 小时前
02-netty基础-java四种IO模型
java·开发语言·nio·jetty