前言
在异步任务、定时器、事件监听和网络回调中,经常会遇到这样的问题:
一个对象需要把自己的成员函数交给其他模块稍后执行,但执行回调时,这个对象可能已经被销毁了。
最直觉的写法是捕获 this:
cpp
executor.post([this] {
handleResult();
});
代码很短,却隐藏着生命周期风险。
C++11 引入了 std::enable_shared_from_this 和 shared_from_this(),让对象可以安全取得共享自己的 shared_ptr。不过 shared_from_this() 会增加强引用计数,可能让对象活得过久,甚至形成循环引用。
C++17 因此为 std::enable_shared_from_this 增加了:
cpp
weak_from_this()
它允许对象取得观察自己的 weak_ptr,但不主动延长自己的生命周期。
本文不重新讲解智能指针的基础用法,而是围绕下面这条主线展开:
text
异步回调为什么不能随便捕获this
↓
为什么shared_ptr(this)更加危险
↓
C++11的shared_from_this解决了什么
↓
shared_from_this为什么有时又太强
↓
C++17的weak_from_this怎样解决问题
↓
它与控制块、引用计数到底怎样关联
↓
构造函数、析构函数、线程和继承中的陷阱
第一章:从一个异步回调场景开始
1.1 对象把任务交给执行器
假设有一个任务执行器:
cpp
class Executor {
public:
void post(std::function<void()> task);
};
post() 不会立刻执行函数,而是先保存任务,稍后在线程池或事件循环中执行。
再定义一个会提交异步任务的类:
cpp
class Session {
public:
explicit Session(Executor& executor)
: executor_(executor) {}
void start() {
executor_.post([this] {
handleResult();
});
}
private:
void handleResult() {
// 处理异步结果
}
Executor& executor_;
};
表面上看没有问题:Lambda 保存 this,将来通过它调用成员函数。
1.2 真正的问题是时间差
可能出现下面的执行顺序:
text
1. 创建Session对象
2. 调用start()提交回调
3. start()返回
4. 外部释放Session对象
5. 执行器开始运行之前保存的回调
6. 回调通过已经失效的this访问对象
第 6 步中的 this 已经成为:
text
Dangling Pointer
= 悬空指针
使用悬空指针会产生:
text
Undefined Behavior
= 未定义行为
程序可能崩溃,也可能读到错误数据,还可能暂时看似正常,因此特别难排查。
1.3 捕获 this 不会延长生命周期
Lambda 中的:
cpp
[this]
只是复制了一个对象地址。它并不知道:
- 对象由谁拥有;
- 对象什么时候销毁;
- 执行回调时对象是否仍然存在。
因此,捕获 this 适用于调用者能够严格保证对象存活的情况;一旦回调可能晚于对象销毁,就必须重新设计生命周期关系。
第二章:直接构造 shared_ptr(this) 为什么更危险
2.1 看似合理的修复
有人会想到:既然裸 this 不管理生命周期,那就在成员函数中把它包装成 shared_ptr:
cpp
void Session::start() {
std::shared_ptr<Session> self(this);
executor_.post([self] {
self->handleResult();
});
}
这段代码通常不是修复,而是在制造更严重的问题。
2.2 一个对象被两个控制块管理
假设对象原本这样创建:
cpp
auto session =
std::make_shared<Session>(executor);
此时已经存在一个 shared_ptr 控制块:
text
控制块A
强引用计数
弱引用计数
对象销毁信息
↓
Session对象
成员函数再执行:
cpp
std::shared_ptr<Session> self(this);
新构造的 shared_ptr 不知道控制块 A 的存在,于是建立控制块 B:
text
控制块A ───┐
├── 都认为自己负责同一个Session对象
控制块B ───┘
当两个控制块的强引用计数分别归零时,它们都会尝试删除同一个对象,可能导致:
text
Double Delete / Double Free
= 重复删除 / 重复释放
2.3 真正需要的是找到原控制块
安全方案不能重新创建一个拥有 this 的控制块,而应该:
找到当前对象原本所属的控制块,再生成一个与原
shared_ptr共享该控制块的新shared_ptr。
C++11 的 std::enable_shared_from_this 就是为此设计的。
第三章:C++11 的 enable_shared_from_this
3.1 基本写法
cpp
class Session
: public std::enable_shared_from_this<Session> {
public:
explicit Session(Executor& executor)
: executor_(executor) {}
private:
Executor& executor_;
};
enable_shared_from_this 可以拆开理解:
text
enable
使某种能力可用
shared_from_this
从this获得共享所有权指针
它是一个类模板,模板参数填写最终需要获得指针的类型:
cpp
std::enable_shared_from_this<Session>
这种"派生类把自己作为基类模板参数"的外形和 CRTP 相似:
text
CRTP
= Curiously Recurring Template Pattern
= 奇异递归模板模式
不过这里的重点不是静态多态,而是让标准库知道应该生成哪种 shared_ptr<T> 和 weak_ptr<T>。
3.2 shared_from_this() 怎样使用
cpp
class Session
: public std::enable_shared_from_this<Session> {
public:
explicit Session(Executor& executor)
: executor_(executor) {}
void start() {
auto self = shared_from_this();
executor_.post([self] {
self->handleResult();
});
}
private:
void handleResult() {}
Executor& executor_;
};
使用时,对象必须先由 shared_ptr 管理:
cpp
auto session =
std::make_shared<Session>(executor);
session->start();
shared_from_this() 返回的新指针与 session 共用同一个控制块:
text
外部session ──────┐
├── 控制块 ── Session对象
回调捕获的self ──┘
回调持有 self 期间,强引用计数不会归零,因此对象不会被销毁。
3.3 它表达的是"任务必须完成"
捕获强引用:
cpp
[self = shared_from_this()] {
self->handleResult();
}
表达的生命周期语义是:
即使所有外部所有者都放弃对象,只要这个回调还没有销毁,对象就必须继续存活。
对于必须完成的写盘、事务提交或关键收尾任务,这可能正是需要的行为。
但并非所有回调都需要这种强保证。
第四章:C++11 方案仍然存在的局限
这里所说的"局限",不是 shared_ptr 设计错误,而是只有强引用式 shared_from_this() 时,难以自然表达某些生命周期语义。
4.1 不重要的回调也会强行延长对象寿命
假设提交的是界面刷新、状态通知或定时统计任务:对象存在时就执行,对象已经销毁时直接放弃即可。
如果仍然捕获:
cpp
auto self = shared_from_this();
那么任务队列会强行保活对象。
text
外部已经不再需要Session
↓
队列中的回调仍持有shared_ptr
↓
Session不能析构
↓
必须等回调从队列中执行或删除
队列积压时,对象可能比预期晚很久才释放。
4.2 对象保存回调时可能形成循环引用
cpp
class Session
: public std::enable_shared_from_this<Session> {
public:
void installCallback() {
callback_ =
[self = shared_from_this()] {
self->handleResult();
};
}
private:
std::function<void()> callback_;
};
所有权关系变成:
text
Session对象
↓ 拥有成员
callback_
↓ 捕获shared_ptr
Session对象
对象通过自己的成员间接拥有自己,强引用计数无法自然归零。
4.3 shared_from_this() 失败时会抛异常
下面的对象没有被 shared_ptr 管理:
cpp
Session session{executor};
session.start();
或者:
cpp
auto session =
std::make_unique<Session>(executor);
session->start();
此时调用:
cpp
shared_from_this()
无法找到共享控制块,从 C++17 起明确抛出:
text
std::bad_weak_ptr
4.4 构造函数中调用得太早
cpp
class Session
: public std::enable_shared_from_this<Session> {
public:
Session() {
auto self = shared_from_this(); // 错误时机
}
};
即使外部写的是:
cpp
auto session = std::make_shared<Session>();
执行 Session 构造函数时,外层 shared_ptr 还没有完成创建,也还没有把内部观察指针连接到控制块。
所以构造函数中的 shared_from_this() 通常会抛出 std::bad_weak_ptr。
4.5 C++11 已经可以手工得到 weak_ptr,但不够直接
C++11 中可以先取得强引用,再转换成弱引用:
cpp
std::weak_ptr<Session> weakSelf =
shared_from_this();
这可以工作,但语义绕了一圈:
text
先构造shared_ptr并短暂增加强引用计数
↓
再构造weak_ptr
↓
临时shared_ptr销毁,强引用计数恢复
更重要的是,如果对象尚未被共享管理,第一步就会抛出异常。
于是 C++17 为这个常见需求增加了直接接口。
第五章:C++17 新增 weak_from_this()
5.1 先澄清版本关系
text
C++11
std::enable_shared_from_this
shared_from_this()
C++17
weak_from_this()
因此,下面这句继承本身不是 C++17 才出现的:
cpp
class Session
: public std::enable_shared_from_this<Session>
C++17 新增的是通过该基类调用:
cpp
weak_from_this()
5.2 基本用法
cpp
class Session
: public std::enable_shared_from_this<Session> {
public:
explicit Session(Executor& executor)
: executor_(executor) {}
void start() {
std::weak_ptr<Session> weakSelf =
weak_from_this();
executor_.post([weakSelf] {
if (auto self = weakSelf.lock()) {
self->handleResult();
}
});
}
private:
void handleResult() {}
Executor& executor_;
};
5.3 它表达的是"对象还在就执行"
这段代码的语义是:
text
提交任务时
回调只保存weak_ptr,不拥有对象
执行任务时
尝试lock()
lock成功
对象仍然存在
临时shared_ptr保证本次调用期间对象不被销毁
lock失败
对象已经销毁
放弃本次回调
它非常适合:
- 延迟通知;
- 非关键定时任务;
- 事件监听;
- 可取消的异步操作;
- 对象销毁后无需继续执行的回调。
5.4 weak_from_this() 不抛出 bad_weak_ptr
它的典型声明可以理解为:
cpp
std::weak_ptr<T>
weak_from_this() noexcept;
std::weak_ptr<const T>
weak_from_this() const noexcept;
如果对象尚未连接到共享控制块,它会返回空 weak_ptr,而不是像 shared_from_this() 那样构造强引用失败并抛出 std::bad_weak_ptr。
因此:
cpp
auto weakSelf = weak_from_this();
if (weakSelf.expired()) {
// 当前没有可共享的对象所有权
}
可以用于非抛异常式观察。
不过,"不抛异常"不等于"构造函数里得到的弱指针以后会自动生效",后面会专门解释这个陷阱。
第六章:shared_from_this 与 weak_from_this 怎样选择
选择标准不是"哪一个更新",而是任务需要表达什么生命周期关系。
6.1 必须完成的任务使用强引用
cpp
void Session::flush() {
executor_.post(
[self = shared_from_this()] {
self->flushData();
}
);
}
含义:
text
任务完成之前,Session必须存活。
6.2 对象存在时才执行的任务使用弱引用
cpp
void Session::scheduleRefresh() {
executor_.post(
[weakSelf = weak_from_this()] {
if (auto self = weakSelf.lock()) {
self->refreshStatus();
}
}
);
}
含义:
text
Session存在就刷新;已经销毁就取消。
6.3 对照表
| 需求 | 推荐方式 |
|---|---|
| 回调必须执行完 | 捕获 shared_from_this() |
| 对象销毁后回调可放弃 | 捕获 weak_from_this() |
| 对象内部长期保存回调 | 通常优先 weak_from_this() |
| 回调只在当前同步调用内使用 | 直接使用引用或 this 也可能足够 |
对象不由 shared_ptr 管理 |
不应依赖这一套机制 |
最重要的问题是:
回调是否应该成为对象的所有者?
如果答案是否定的,就不应该因为写起来方便而捕获强引用。
第七章:不要分开调用 expired() 和 lock()
7.1 有竞争窗口的写法
cpp
if (!weakSelf.expired()) {
auto self = weakSelf.lock();
self->handleResult();
}
在多线程环境中可能发生:
text
线程A:expired()返回false
线程B:释放最后一个shared_ptr,对象销毁
线程A:lock()返回空指针
线程A:解引用空指针
expired() 的结果只代表检查发生的那个瞬间。
7.2 直接检查 lock() 的结果
正确方式:
cpp
if (auto self = weakSelf.lock()) {
self->handleResult();
}
lock() 会尝试原子地取得一个强引用:
text
成功
返回非空shared_ptr
在self作用域结束前对象保持存活
失败
返回空shared_ptr
不访问对象
这解决的是对象生命周期竞争,但不自动保证对象成员数据线程安全。
即使 lock() 成功,多个线程同时修改成员仍然可能需要互斥量、原子变量或其他同步机制。
第八章:构造函数中的关键陷阱
8.1 构造期间内部弱指针还没有连接控制块
cpp
class Session
: public std::enable_shared_from_this<Session> {
public:
Session() {
auto weakSelf = weak_from_this();
// 此时通常为空
}
};
weak_from_this() 不会抛异常,但返回的通常是空弱指针。
8.2 提前取得的空 weak_ptr 不会自动更新
下面的写法尤其容易误解:
cpp
class Session
: public std::enable_shared_from_this<Session> {
public:
Session()
: self_(weak_from_this()) {}
private:
std::weak_ptr<Session> self_;
};
有人可能认为:
text
现在self_为空没关系,
等make_shared完成后它会自动连上控制块。
实际并不会。
构造函数中复制出来的 self_ 是一个独立的空 weak_ptr。之后 enable_shared_from_this 内部的观察指针虽然会被初始化,但之前复制出去的空 weak_ptr 不会自动改绑。
8.3 使用创建后初始化
把需要 weak_from_this() 的逻辑放到对象被 shared_ptr 接管之后:
cpp
class Session
: public std::enable_shared_from_this<Session> {
public:
explicit Session(Executor& executor)
: executor_(executor) {}
void initialize() {
weakSelf_ = weak_from_this();
}
private:
Executor& executor_;
std::weak_ptr<Session> weakSelf_;
};
auto session =
std::make_shared<Session>(executor);
session->initialize();
但公开 initialize() 容易被忘记调用。工程中可以通过工厂函数封装两阶段初始化。
第九章:用工厂函数保证正确初始化顺序
9.1 工厂负责完成两件事
text
第一步:让shared_ptr接管对象
第二步:执行需要weak_from_this的初始化
示例:
cpp
class Session
: public std::enable_shared_from_this<Session> {
public:
static std::shared_ptr<Session>
create(Executor& executor) {
struct MakeSharedEnabler
: public Session {
explicit MakeSharedEnabler(
Executor& executor
)
: Session(executor) {}
};
auto session =
std::make_shared<MakeSharedEnabler>(
executor
);
session->initialize();
return session;
}
void start() {
executor_.post(
[weakSelf = weak_from_this()] {
if (auto self = weakSelf.lock()) {
self->handleResult();
}
}
);
}
protected:
explicit Session(Executor& executor)
: executor_(executor) {}
private:
void initialize() {
// 此时已经存在shared_ptr控制块
weakSelf_ = weak_from_this();
}
void handleResult() {}
Executor& executor_;
std::weak_ptr<Session> weakSelf_;
};
使用者只能通过:
cpp
auto session = Session::create(executor);
拿到对象,工厂保证初始化顺序正确。
9.2 为什么使用 MakeSharedEnabler
如果构造函数是 private 或 protected,std::make_shared<Session> 的标准库内部代码通常没有访问权限。
局部辅助类公开转发构造,再由 make_shared 创建辅助类,可以继续获得合并分配等优势,同时限制外部随意构造 Session。
这种写法不是 weak_from_this() 的硬性要求,只是封装两阶段初始化的一种常见工程手法。
第十章:enable_shared_from_this 的内部原理
10.1 简化模型
标准库的具体实现不完全相同,但可以把它粗略理解为:
cpp
template<class T>
class enable_shared_from_this {
public:
std::shared_ptr<T> shared_from_this() {
return std::shared_ptr<T>(weakThis_);
}
std::weak_ptr<T>
weak_from_this() noexcept {
return weakThis_;
}
protected:
constexpr enable_shared_from_this()
noexcept = default;
private:
mutable std::weak_ptr<T> weakThis_;
};
真实标准库还会包含 const 重载、友元关系和实现细节。
10.2 谁负责初始化内部的 weakThis_
不是对象构造函数自己初始化,而是第一个正确接管对象的 shared_ptr 完成连接。
cpp
auto session =
std::make_shared<Session>(executor);
shared_ptr 创建过程中会检测:
text
Session是否拥有一个公开且无歧义的
enable_shared_from_this<Session>基类?
如果满足条件,就把基类内部的观察指针连接到新控制块:
text
shared_ptr<Session>
│
▼
控制块
▲
│
enable_shared_from_this内部weakThis_
10.3 两个成员函数只是从内部弱指针生成不同结果
text
shared_from_this()
从内部weak_ptr构造shared_ptr
成功时增加强引用计数
没有控制块时抛bad_weak_ptr
weak_from_this()
复制内部weak_ptr
不增加强引用计数
没有控制块时得到空weak_ptr
10.4 为什么基类通常必须 public 继承
正确写法:
cpp
class Session
: public std::enable_shared_from_this<Session> {
};
错误倾向:
cpp
class Session
: private std::enable_shared_from_this<Session> {
};
标准库需要从外部检测并访问这个基类。private 继承可能使这条转换不可访问,导致内部弱指针没有按预期连接控制块。
因此应使用公开、无歧义继承。
第十一章:继承层次中的类型问题
11.1 基类继承后,返回的是基类指针
cpp
class Base
: public std::enable_shared_from_this<Base> {
};
class Derived : public Base {
};
在 Derived 对象中调用:
cpp
shared_from_this()
返回类型仍然是:
cpp
std::shared_ptr<Base>
因为模板参数写的是 Base。
如果确实需要 shared_ptr<Derived>,可以在确认真实类型后转换:
cpp
auto derived =
std::dynamic_pointer_cast<Derived>(
shared_from_this()
);
或者让真正需要"获得自己"的最终类型继承相应的 enable_shared_from_this<Derived>。不要在复杂继承体系中重复放置多个有歧义的 enable_shared_from_this 基类。
11.2 不要让同一对象产生多个控制块
无论是否继承 enable_shared_from_this,下面的根本规则都不能破坏:
同一个动态对象只能由同一套共享所有权控制块负责销毁。
危险示例:
cpp
Session* raw = new Session(executor);
std::shared_ptr<Session> first(raw);
std::shared_ptr<Session> second(raw);
两个从同一裸指针分别构造的 shared_ptr 不会自动合并控制块。
enable_shared_from_this 能让对象找到已经建立的控制块,但不能修复人为创建多个独立控制块的错误。
第十二章:析构期间不要试图"复活"对象
12.1 析构开始意味着强所有权已经结束
对象析构通常发生在最后一个强引用释放之后。
此时即使能够取得内部弱指针:
cpp
auto weakSelf = weak_from_this();
它的 lock() 通常也不能重新得到有效强引用,因为强引用计数已经归零,对象的销毁已经开始。
12.2 不要在析构函数中注册新的自身回调
cpp
Session::~Session() {
executor_.post(
[weakSelf = weak_from_this()] {
// 不应期待以后还能恢复Session
}
);
}
析构函数应该完成资源释放和注销,不应该试图通过新的共享所有权让对象复活。
如果需要异步收尾,应在对象仍然有效时显式启动,并用清晰的状态机或外部管理器协调。
第十三章:线程安全到底保证到哪一层
13.1 lock() 保护的是生命周期
cpp
if (auto self = weakSelf.lock()) {
self->handleResult();
}
一旦 lock() 成功,self 在当前作用域内持有强引用,因此对象不会在函数执行一半时被其他线程销毁。
13.2 它不保护对象的业务数据
下面两个线程都可能成功取得 self:
cpp
// 线程A
if (auto self = weakSelf.lock()) {
self->updateState();
}
// 线程B
if (auto self = weakSelf.lock()) {
self->updateState();
}
如果 updateState() 修改共享成员,仍然可能发生数据竞争。
需要另外使用:
std::mutex;std::atomic;- 串行执行器;
- Actor 模型;
- 明确的线程归属约束。
可以记住:
text
weak_ptr::lock()
解决"对象还活着吗"
mutex或其他同步机制
解决"多个线程能同时改数据吗"
第十四章:完整的 C++17 示例
下面用一个简单任务队列展示完整流程。示例故意把任务延迟到外部主动执行,以便观察对象销毁后的行为。
cpp
#include <functional>
#include <iostream>
#include <memory>
#include <queue>
#include <utility>
class Executor {
public:
void post(std::function<void()> task) {
tasks_.push(std::move(task));
}
void runAll() {
while (!tasks_.empty()) {
auto task = std::move(tasks_.front());
tasks_.pop();
task();
}
}
private:
std::queue<std::function<void()>> tasks_;
};
class Session
: public std::enable_shared_from_this<Session> {
public:
explicit Session(Executor& executor)
: executor_(executor) {
std::cout << "Session构造\n";
}
~Session() {
std::cout << "Session析构\n";
}
void postOptionalTask() {
executor_.post(
[weakSelf = weak_from_this()] {
if (auto self = weakSelf.lock()) {
self->handleResult();
} else {
std::cout
<< "对象已销毁,取消任务\n";
}
}
);
}
void postRequiredTask() {
executor_.post(
[self = shared_from_this()] {
self->handleResult();
}
);
}
private:
void handleResult() {
std::cout << "执行Session任务\n";
}
Executor& executor_;
};
int main() {
Executor executor;
{
auto session =
std::make_shared<Session>(executor);
session->postOptionalTask();
}
std::cout << "运行可取消任务:\n";
executor.runAll();
{
auto session =
std::make_shared<Session>(executor);
session->postRequiredTask();
}
std::cout << "运行必须完成的任务:\n";
executor.runAll();
}
第一段使用 weak_from_this():
text
离开作用域
↓
外部shared_ptr销毁
↓
回调只有weak_ptr,不拥有对象
↓
Session立即析构
↓
执行任务时lock失败,取消任务
第二段使用 shared_from_this():
text
离开作用域
↓
外部shared_ptr销毁
↓
回调仍持有shared_ptr
↓
Session继续存活
↓
任务执行完成,回调销毁
↓
最后一个shared_ptr释放,Session析构
两个接口没有绝对的优劣,它们表达的是两种不同生命周期策略。
第十五章:常见错误清单
15.1 错把整个特性当成 C++17 新增
text
enable_shared_from_this和shared_from_this
C++11已有
weak_from_this
C++17新增
15.2 使用 shared_ptr(this)
它可能创建第二个控制块,导致重复释放。应使用 shared_from_this() 获取共享原控制块的指针。
15.3 对栈对象调用 shared_from_this()
cpp
Session session{executor};
session.shared_from_this();
对象没有共享控制块,会失败并抛出 std::bad_weak_ptr。
15.4 在构造函数中调用 shared_from_this()
此时控制块尚未完成连接,调用时机过早。
15.5 认为构造函数中取得的 weak_ptr 以后会自动有效
构造期间复制出的空 weak_ptr 不会在外层 shared_ptr 创建完成后自动改绑。应进行创建后初始化。
15.6 忘记检查 lock() 结果
cpp
auto self = weakSelf.lock();
self->run(); // self可能为空
应写成:
cpp
if (auto self = weakSelf.lock()) {
self->run();
}
15.7 先检查 expired() 再无条件使用 lock()
两步之间存在竞争窗口。直接检查 lock() 返回值。
15.8 认为 lock() 能保证成员线程安全
它只保证对象生命周期,不替代互斥量和线程同步。
15.9 使用 private 继承
cpp
class Session
: private std::enable_shared_from_this<Session> {
};
可能阻止标准库正确连接内部观察指针。应使用 public、无歧义继承。
15.10 无条件使用弱引用
如果任务必须完成,弱引用可能让任务在对象销毁后静默取消。应该先决定生命周期语义,再选择强引用还是弱引用。
第十六章:最终总结
std::enable_shared_from_this<T> 解决的核心问题是:
已经由
shared_ptr管理的对象,怎样在成员函数中找到原来的控制块,而不是围绕this创建第二个控制块。
C++11 提供:
cpp
shared_from_this()
它生成共享同一控制块的强引用,适用于:
text
任务必须完成
回调期间必须保活对象
C++17 新增:
cpp
weak_from_this()
它生成观察同一控制块的弱引用,适用于:
text
对象存在就执行
对象销毁就取消
不希望回调延长对象寿命
希望避免自身循环引用
把完整机制连起来:
text
类public继承enable_shared_from_this<T>
↓
shared_ptr第一次正确接管对象
↓
标准库把内部weak_ptr连接到控制块
↓
shared_from_this()从内部weak_ptr取得强引用
↓
weak_from_this()复制内部weak_ptr作为观察者
↓
回调执行时通过lock()安全确认对象是否仍然存在
最终需要记住五条规则:
- 不要使用
shared_ptr(this)创建自身共享指针。 shared_from_this()会保活对象,weak_from_this()不会。- 对象必须先被
shared_ptr正确接管,内部观察指针才会连接控制块。 - 构造函数中调用得太早,必要时使用工厂和创建后初始化。
- 多线程回调中直接检查
lock()结果,但还要另外保护业务数据。
真正重要的不是机械地把所有回调都换成 weak_from_this(),而是先回答:
这个回调应不应该拥有对象?
答案决定了应该使用强引用保活,还是使用弱引用观察。