8人参与 • 2025-06-13 • C/C++
weak_ptr
是 c++11 引入的一种智能指针,它与 shared_ptr
配合使用,主要解决以下问题:
shared_ptr
之间的循环引用导致内存泄漏weak_ptr
与 shared_ptr
共享同一个控制块,控制块包含:
struct controlblock { std::atomic<size_t> shared_count; // 强引用计数 std::atomic<size_t> weak_count; // 弱引用计数 // 其他元数据(删除器、分配器等) };
shared_count
归零时,管理对象被销毁shared_count
和 weak_count
都归零时,控制块被释放weak_count
,不影响 shared_count
// 创建 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) // 控制块被释放
// 从 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;
// 检查对象是否有效 if (!wp.expired()) { // 尝试升级为 shared_ptr if (auto sp = wp.lock()) { // 使用 sp 安全访问对象 } }
// 监控资源释放 std::weak_ptr<myclass> wp; { auto sp = std::make_shared<myclass>(); wp = sp; // 对象存在 } // 此时 wp.expired() == true
weak_count
的增减是线程安全的shared_ptr
的过程是原子的lock()
获取的 shared_ptr
需要额外同步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; }
class parent { std::shared_ptr<child> child; }; class child { std::weak_ptr<parent> parent; // 使用 weak_ptr 避免循环 };
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; } };
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); // 移除无效观察者 } } } };
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 }
不能直接解引用:必须先用 lock()
升级为 shared_ptr
// 错误用法 // *wp; // 编译错误 // 正确用法 if (auto sp = wp.lock()) { *sp = value; }
性能考虑:
lock()
操作包含原子操作,有一定开销构造函数限制:
// 不能直接从裸指针构造 // std::weak_ptr<int> wp(new int(42)); // 错误 // 必须从 shared_ptr 构造 auto sp = std::make_shared<int>(42); std::weak_ptr<int> wp(sp); // 正确
避免频繁 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:对象和控制块单次分配,减少内存碎片
特性 | weak_ptr | shared_ptr | unique_ptr |
---|---|---|---|
所有权 | 无 | 共享 | 独占 |
影响生命周期 | 否 | 是 | 是 |
直接访问对象 | 需通过 lock() | 可直接访问 | 可直接访问 |
引用计数 | 只增加 weak_count | 增加 shared_count | 无 |
典型用途 | 打破循环引用/观察 | 共享所有权 | 独占所有权 |
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
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);
weak_ptr
的核心原理可以总结为:
shared_ptr
共享同一控制块,维护 weak_count
lock()
原子操作获取可用的 shared_ptr
weak_ptr
析构后,控制块被释放正确使用 weak_ptr
可以:
理解 weak_ptr
的工作原理对于设计复杂的内存管理系统和避免资源泄漏至关重要,它是现代 c++ 高效内存管理工具链中不可或缺的一环。
到此这篇关于c++中智能指针weak_ptr的原理及使用的文章就介绍到这了,更多相关c++ weak_ptr内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论