1. weak_ptr 的基本概念
weak_ptr 是 c++11 引入的一种智能指针,它与 shared_ptr 配合使用,主要解决以下问题:
- 打破循环引用:防止
shared_ptr之间的循环引用导致内存泄漏 - 安全观察:允许观察共享对象而不影响其生命周期
- 避免悬空指针:可以检测被观察对象是否已被释放
2. weak_ptr 的核心原理
2.1 控制块结构
weak_ptr 与 shared_ptr 共享同一个控制块,控制块包含:
struct controlblock {
std::atomic<size_t> shared_count; // 强引用计数
std::atomic<size_t> weak_count; // 弱引用计数
// 其他元数据(删除器、分配器等)
};
2.2 内存管理规则
- 对象销毁条件:当
shared_count归零时,管理对象被销毁 - 控制块销毁条件:当
shared_count和weak_count都归零时,控制块被释放 - weak_ptr 不参与所有权:仅增加
weak_count,不影响shared_count
2.3 工作流程
// 创建 shared_ptr(控制块:shared=1, weak=0) auto sp = std::make_shared<int>(42); // 创建 weak_ptr(控制块:shared=1, weak=1) std::weak_ptr<int> wp = sp; // shared_ptr 析构(控制块:shared=0, weak=1) sp.reset(); // 此时: // - 管理的 int 对象已被销毁 // - 控制块仍然存在(weak_count=1) // - wp.expired() == true // weak_ptr 析构(控制块:shared=0, weak=0) // 控制块被释放
3. weak_ptr 的关键操作
3.1 创建与赋值
// 从 shared_ptr 创建 auto sp = std::make_shared<myclass>(); std::weak_ptr<myclass> wp1(sp); // 拷贝构造 std::weak_ptr<myclass> wp2(wp1); // 赋值操作 std::weak_ptr<myclass> wp3; wp3 = wp1;
3.2 检查与升级
// 检查对象是否有效
if (!wp.expired()) {
// 尝试升级为 shared_ptr
if (auto sp = wp.lock()) {
// 使用 sp 安全访问对象
}
}
3.3 资源释放监控
// 监控资源释放
std::weak_ptr<myclass> wp;
{
auto sp = std::make_shared<myclass>();
wp = sp;
// 对象存在
}
// 此时 wp.expired() == true
4. weak_ptr 的线程安全性
4.1 官方标准规定
- 控制块操作是原子的:
weak_count的增减是线程安全的 - lock() 操作是线程安全的:升级为
shared_ptr的过程是原子的 - 对象访问需要同步:通过
lock()获取的shared_ptr需要额外同步
4.2 线程安全示例
std::shared_ptr<int> sp = std::make_shared<int>(42);
std::weak_ptr<int> wp(sp);
// 线程1
if (auto local_sp = wp.lock()) {
std::lock_guard<std::mutex> lock(mtx);
*local_sp = 10;
}
// 线程2
if (auto local_sp = wp.lock()) {
std::lock_guard<std::mutex> lock(mtx);
int val = *local_sp;
}
5. weak_ptr 的典型应用场景
5.1 打破循环引用
class parent {
std::shared_ptr<child> child;
};
class child {
std::weak_ptr<parent> parent; // 使用 weak_ptr 避免循环
};
5.2 缓存系统
class cache {
std::unordered_map<key, std::weak_ptr<resource>> cache;
std::shared_ptr<resource> get(key key) {
if (auto it = cache.find(key); it != cache.end()) {
if (auto sp = it->second.lock()) {
return sp; // 缓存命中
}
cache.erase(it); // 清理过期缓存
}
// 缓存未命中,创建新资源
auto sp = std::make_shared<resource>(key);
cache[key] = sp;
return sp;
}
};
5.3 观察者模式
class subject {
std::vector<std::weak_ptr<observer>> observers;
void notify() {
for (auto it = observers.begin(); it != observers.end(); ) {
if (auto obs = it->lock()) {
obs->update();
++it;
} else {
it = observers.erase(it); // 移除无效观察者
}
}
}
};
6. weak_ptr 的实现细节
6.1 控制块生命周期
6.2 lock() 的原子实现
lock() 操作必须保证线程安全,伪代码实现:
shared_ptr<t> lock() const noexcept {
controlblock* cb = get_control_block();
size_t sc = cb->shared_count.load();
do {
if (sc == 0) return nullptr; // 对象已释放
// 尝试增加 shared_count(cas操作)
} while (!cb->shared_count.compare_exchange_weak(sc, sc + 1));
return shared_ptr<t>(cb); // 创建新的 shared_ptr
}
7. weak_ptr 的注意事项
不能直接解引用:必须先用 lock() 升级为 shared_ptr
// 错误用法
// *wp; // 编译错误
// 正确用法
if (auto sp = wp.lock()) {
*sp = value;
}
性能考虑:
lock()操作包含原子操作,有一定开销- 控制块需要额外内存(通常16-32字节)
构造函数限制:
// 不能直接从裸指针构造 // std::weak_ptr<int> wp(new int(42)); // 错误 // 必须从 shared_ptr 构造 auto sp = std::make_shared<int>(42); std::weak_ptr<int> wp(sp); // 正确
8. 性能优化建议
避免频繁 lock/unlock:在需要时缓存 shared_ptr
void process(std::weak_ptr<data> wp) {
auto sp = wp.lock(); // 只调用一次 lock
if (!sp) return;
// 多次使用 sp 而不重复调用 lock
sp->operation1();
sp->operation2();
}
及时清理失效 weak_ptr:定期检查并移除 expired() 的 weak_ptr
考虑使用 make_shared:对象和控制块单次分配,减少内存碎片
9. 与其它智能指针的对比
| 特性 | weak_ptr | shared_ptr | unique_ptr |
|---|---|---|---|
| 所有权 | 无 | 共享 | 独占 |
| 影响生命周期 | 否 | 是 | 是 |
| 直接访问对象 | 需通过 lock() | 可直接访问 | 可直接访问 |
| 引用计数 | 只增加 weak_count | 增加 shared_count | 无 |
| 典型用途 | 打破循环引用/观察 | 共享所有权 | 独占所有权 |
10. 现代 c++ 中的增强
10.1 c++17 的 weak_from_this
class myclass : public std::enable_shared_from_this<myclass> {
public:
std::weak_ptr<myclass> get_weak() {
return weak_from_this(); // 安全获取 weak_ptr
}
};
auto obj = std::make_shared<myclass>();
auto wobj = obj->get_weak(); // 安全获取 weak_ptr
10.2 c++20 的原子 shared_ptr/weak_ptr
std::atomic<std::weak_ptr<int>> atomic_wp; atomic_wp.store(wp, std::memory_order_release); auto current = atomic_wp.load(std::memory_order_acquire);
11. 总结
weak_ptr 的核心原理可以总结为:
- 不参与所有权:仅观察不拥有,不影响对象生命周期
- 共享控制块:与
shared_ptr共享同一控制块,维护weak_count - 安全升级:通过
lock()原子操作获取可用的shared_ptr - 自动清理:当最后一个
weak_ptr析构后,控制块被释放
正确使用 weak_ptr 可以:
- 有效解决循环引用导致的内存泄漏
- 实现安全的对象观察模式
- 构建高效的缓存系统
- 开发更健壮的异步代码
理解 weak_ptr 的工作原理对于设计复杂的内存管理系统和避免资源泄漏至关重要,它是现代 c++ 高效内存管理工具链中不可或缺的一环。
到此这篇关于c++中智能指针weak_ptr的原理及使用的文章就介绍到这了,更多相关c++ weak_ptr内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论