在lambda表达式用*this捕获对象副本

在lambda表达式用*this捕获对象副本

C++17 允许在 lambda 捕获列表中使用 *this。它的含义不是捕获 this 指针,而是按值捕获当前对象的一个副本。这个特性主要用来解决 lambda 延迟执行时,原对象已经变化甚至已经销毁带来的问题。

1. 先看 this 捕获是什么

在类成员函数里,lambda 很常见:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Test
{
public:
void func()
{
auto f = [this]() {
value++;
};
}

private:
int value = 0;
};

这里捕获的是 this 指针。
也就是说,lambda 里面访问的仍然是原对象本身。

2. *this 又是什么?

[*this] 则不同,它会把当前对象拷贝一份到 lambda 里。

1
2
3
auto f = [*this]() {
return value;
};

这样 lambda 内部操作的是对象副本,不是原对象。

3. 为什么这个特性重要?

如果 lambda 只是当前函数里立即执行,那么 [this] 一般没什么问题。
但如果 lambda 会被保存起来、异步执行、或者放到线程里延后执行,问题就来了:

  • 原对象可能已经销毁
  • 原对象的数据可能已经被修改

[*this] 能让 lambda 拿到一个当时的快照副本,减少这类风险。

4. 基本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
using namespace std;

class Worker
{
public:
Worker(int v) : value(v) {}

auto getLambda() const
{
return [*this]() {
cout << value << endl;
};
}

private:
int value;
};

int main()
{
Worker w(10);
auto task = w.getLambda();
task();
return 0;
}

这里即使后面原对象状态变化,lambda 内拿到的仍然是创建时拷贝进去的值。

5. 和 [this] 的区别

[this]

  • 捕获的是指针
  • 访问的是原对象
  • 原对象变化,lambda 看到的内容也会变化
  • 原对象销毁后继续调用 lambda,可能有风险

[*this]

  • 捕获的是对象副本
  • 访问的是拷贝后的对象
  • 原对象后续变化,不影响 lambda 内部状态
  • 更适合异步或延迟执行场景

6. 一个更直观的对比例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
using namespace std;

class Demo
{
public:
int value = 10;

auto byThis()
{
return [this]() {
cout << "this: " << value << endl;
};
}

auto byCopy()
{
return [*this]() {
cout << "*this: " << value << endl;
};
}
};

int main()
{
Demo d;
auto f1 = d.byThis();
auto f2 = d.byCopy();

d.value = 99;

f1();
f2();
return 0;
}

输出通常会表现为:

1
2
this: 99
*this: 10

这就很清楚了。

7. 适合用在什么场景?

7.1 异步任务

如果 lambda 交给线程池、回调队列或者异步接口执行,用 [*this] 往往更安全。

7.2 需要保留对象创建当时状态

有些逻辑本来就需要一个“快照”,而不是实时访问当前对象。

7.3 避免悬空指针风险

当原对象生命周期不容易保证时,值捕获副本通常更稳。

8. 使用时的注意点

8.1 会发生对象拷贝

这一点不能忽略。
如果对象很大,或者拷贝代价很高,那 [*this] 可能并不合适。

8.2 副本和原对象不是同一个对象

你在 lambda 里改副本,并不会影响外部原对象。

8.3 对象必须可拷贝

如果类禁用了拷贝构造,那 [*this] 就不能直接这样用。

总结

[*this] 可以理解成“把当前对象按值打包进 lambda”。
它和 [this] 最大的区别不在语法,而在语义:前者拿的是副本,后者拿的是原对象地址。对于异步回调、延迟执行和状态快照场景,这个特性还是很实用的。