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

相关推荐
John_ToDebug2 分钟前
JS 与 C++ 双向通信实战:基于 WebHostViewListener 的消息处理机制
前端·c++·chrome
papership9 分钟前
【入门级-C++程序设计:11、指针与引用-引 用】
c语言·开发语言·c++·青少年编程
岁忧12 分钟前
(LeetCode 每日一题) 1780. 判断一个数字是否可以表示成三的幂的和 (数学、三进制数)
java·c++·算法·leetcode·职场和发展·go
常乐か1 小时前
VS2022+QT5.15.2+OCCT7.9.1的开发环境搭建流程
开发语言·qt·opencascade
Evand J2 小时前
【MATLAB例程】滑动窗口均值滤波、中值滤波、最小值/最大值滤波对比。附代码下载链接
开发语言·matlab·均值算法
IT毕设实战小研2 小时前
基于SpringBoot的救援物资管理系统 受灾应急物资管理系统 物资管理小程序
java·开发语言·vue.js·spring boot·小程序·毕业设计·课程设计
yzx9910132 小时前
PHP 开发全解析:从基础到实战的进阶之路
开发语言·php
rhythmcc3 小时前
【visual studio】visual studio配置环境opencv和onnxruntime
c++·人工智能·opencv
gAlAxy...3 小时前
Java List 集合详解(ArrayList、LinkedList、Vector)
java·开发语言