std::weak_ptr 实现分析
std::weak_ptr是观察std::shared_ptr管理对象的弱引用,不增加引用计数。本文基于 GCC 和 LLVM 的源码,分析 weak_ptr 的内部实现。
一、核心概念
1.1 什么是 weak_ptr
weak_ptr 是一种不拥有对象的智能指针,观察 shared_ptr 管理的对象:
cpp
// weak_ptr 的基本使用
shared_ptr<int> sp = make_shared<int>(42);
weak_ptr<int> wp = sp; // 观察 sp 管理的对象
// 使用前需要 lock
if (auto locked = wp.lock()) {
cout << *locked << endl; // 安全访问
} else {
cout << "对象已销毁" << endl;
}1.2 weak_ptr vs shared_ptr
weak_ptr vs shared_ptr:
shared_ptr:
· 拥有对象
· 增加引用计数
· 控制对象生命周期
weak_ptr:
· 观察对象
· 不增加引用计数
· 不影响对象生命周期
· 需要 lock() 才能访问二、核心数据结构
2.1 弱引用计数
weak_ptr 与 shared_ptr 共享控制块,但使用独立的弱引用计数:
控制块布局:
┌─────────────────────────────────────┐
│ vptr(虚函数表指针) │
├─────────────────────────────────────┤
│ use_count(强引用计数) │
├─────────────────────────────────────┤
│ weak_count(弱引用计数) │
├─────────────────────────────────────┤
│ 删除器(可选) │
└─────────────────────────────────────┘
引用计数的语义:
· use_count:有多少个 shared_ptr 拥有对象
· weak_count:有多少个 weak_ptr 观察对象 + 1(如果 use_count > 0)2.2 GCC (libstdc++) 的实现(源码分析)
cpp
// 源码路径:references/impl/gcc/libstdc++-v3/include/bits/shared_ptr_base.h:200
// 弱引用计数操作
void _M_weak_add_ref() noexcept {
// 弱引用计数可以使用负值(用户不可观察)
constexpr _Atomic_word __max = -1;
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, 1) == __max)
[[__unlikely__]] __builtin_trap(); // 溢出保护
}
void _M_weak_release() noexcept {
// 使用 acq_rel 内存序,确保 dispose() 的结果在 destroy() 中可见
_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1) {
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);
if (_Mutex_base<_Lp>::_S_need_barriers) {
// 需要内存屏障:确保 dispose() 的结果在 destroy() 中可见
__atomic_thread_fence(__ATOMIC_ACQ_REL);
}
_M_destroy(); // 销毁控制块
}
}
// weak_count 的语义:
// weak_count = weak_ptr 数量 + (use_count > 0 ? 1 : 0)
// 当 weak_count == 0 时,销毁控制块2.3 LLVM (libc++) 的实现(源码分析)
cpp
// 源码路径:references/impl/llvm-project/libcxx/include/__memory/shared_ptr.h
// LLVM 的弱引用计数操作
void __add_weak() _NOEXCEPT {
__libcpp_atomic_refcount_increment(__weak_shared_owners_);
}
void __release_weak() _NOEXCEPT {
if (__libcpp_atomic_refcount_decrement(__weak_shared_owners_) == -1) {
// weak_count 归零,销毁控制块
__on_zero_shared_weak();
}
}
---
## 三、lock() 的实现
### 3.1 lock 的工作原理
```cpp
// lock 的伪代码
shared_ptr<T> lock() const noexcept {
if (use_count() == 0) {
return shared_ptr<T>(); // 返回空指针
}
// 增加引用计数(原子操作)
// 如果在增加过程中 use_count 变为 0,返回空指针
return shared_ptr<T>(*this);
}3.2 竞态条件处理
lock() 的竞态条件:
线程 A: 线程 B:
sp.reset() wp.lock()
↓ ↓
use_count-- = 1 use_count == 1?
↓ ↓
use_count == 0 增加引用计数
↓ ↓
调用析构函数 返回有效的 shared_ptr
关键:原子操作保证 use_count 的一致性四、GCC vs LLVM 差异对比
┌────────────────────────┬──────────────────────┬──────────────────────┐
│ 特性 │ GCC (libstdc++) │ LLVM (libc++) │
├────────────────────────┼──────────────────────┼──────────────────────┤
│ 弱引用计数 │ _M_weak_count │ __weak_shared_owners_│
│ lock 实现 │ 原子操作 │ 原子操作 │
│ expired 检查 │ use_count() == 0 │ use_count() == 0 │
│ enable_shared_from_this│ 支持 │ 支持 │
│ owner_before │ 支持 │ 支持 │
└────────────────────────┴──────────────────────┴──────────────────────┘五、enable_shared_from_this
5.1 什么是 enable_shared_from_this
enable_shared_from_this 允许对象在 shared_ptr 管理下安全地获取自身的 shared_ptr:
cpp
class MyClass : public enable_shared_from_this<MyClass> {
public:
shared_ptr<MyClass> get_self() {
return shared_from_this(); // 安全获取自身的 shared_ptr
}
};5.2 实现原理
enable_shared_from_this 的实现:
1. 内部持有一个 weak_ptr
2. 构造函数中将 weak_ptr 绑定到 shared_ptr
3. shared_from_this() 调用 weak_ptr::lock()
内存布局:
┌─────────────────────────────────────┐
│ MyClass 对象 │
│ ┌─────────────────────────────┐ │
│ │ weak_ptr<MyClass> __weak_this │ ← 内部 weak_ptr
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘六、最佳实践
weak_ptr 使用指南:
1. 观察者模式:
· 观察者使用 weak_ptr
· 被观察者使用 shared_ptr
· 避免循环引用
2. 缓存:
· 使用 weak_ptr 缓存对象
· 对象销毁后自动失效
3. 避免悬垂指针:
· 使用前先 lock()
· 检查 lock() 是否成功
4. 不要存储 weak_ptr 到栈上对象:
· weak_ptr 只能观察堆上对象
· 栈上对象不需要 weak_ptr延伸阅读
- std::shared_ptr 实现 — shared_ptr 的详细实现
- std::unique_ptr 实现 — 唯一所有权的智能指针
- 分配器模型 — allocator 如何与智能指针交互