016 C++的weak_ptr


作者Lou Xiao, deepseek创建时间2025-04-02 14:16:16更新时间2025-04-02 14:16:16

1. 概述

std::weak_ptr 是 C++11 引入的智能指针,用于解决 std::shared_ptr 的循环引用问题。它是一种"弱引用"指针,不增加引用计数,不影响对象的生命周期。

2. 基本特性

2.1 主要特点

  • 不拥有对象的所有权
  • 不增加引用计数
  • 可以从 std::shared_ptr 创建
  • 可以检测所指向的对象是否已被释放

2.2 与 shared_ptr 的关系

  • weak_ptrshared_ptr 的配套工具
  • 必须与 shared_ptr 配合使用
  • 用于观察 shared_ptr 管理的资源而不影响其生命周期

3. 核心操作

3.1 构造与赋值

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // 默认构造
2 std::weak_ptr<T> wp1;
3
4 // 从 shared_ptr 构造
5 std::shared_ptr<T> sp(new T);
6 std::weak_ptr<T> wp2(sp);
7
8 // 拷贝构造
9 std::weak_ptr<T> wp3(wp2);
10
11 // 赋值操作
12 wp1 = sp;
13 wp1 = wp2;

3.2 关键方法

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // 获取关联的 shared_ptr
2 std::shared_ptr<T> locked = wp.lock();
3
4 // 检查引用计数
5 long use_count = wp.use_count(); // 可能已过期
6
7 // 检查是否为空
8 bool expired = wp.expired(); // use_count == 0

3.3 重置操作

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 wp.reset(); // 释放观察权,不再指向任何对象

4. 典型应用场景

4.1 解决循环引用

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 struct Node {
2 std::shared_ptr<Node> next;
3 std::weak_ptr<Node> prev; // 使用 weak_ptr 打破循环
4 };

4.2 缓存实现

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 class Cache {
2 std::unordered_map<Key, std::weak_ptr<Value>> cache;
3 public:
4 std::shared_ptr<Value> get(Key key) {
5 auto it = cache.find(key);
6 if (it != cache.end()) {
7 return it->second.lock(); // 转为 shared_ptr 或 nullptr
8 }
9 return nullptr;
10 }
11 };

4.3 观察者模式

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 class Subject {
2 std::vector<std::weak_ptr<Observer>> observers;
3 public:
4 void notify() {
5 for (auto& wp : observers) {
6 if (auto sp = wp.lock()) {
7 sp->update();
8 }
9 }
10 }
11 };

5. 注意事项

5.1 线程安全

  • weak_ptr 本身的操作是原子的
  • lock() 和后续使用不是原子的组合操作
  • 多线程环境下需要额外同步

5.2 性能考虑

  • weak_ptrshared_ptr 构造和拷贝成本略高
  • lock() 操作需要原子操作,有一定开销

5.3 使用限制

  • 不能直接解引用 (operator*operator->)
  • 必须通过 lock() 转为 shared_ptr 后才能访问对象

6. 实现原理

6.1 控制块结构

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 +---------------------+
2 | Control Block |
3 | +-----------------+ |
4 | | strong refcount | |
5 | +-----------------+ |
6 | | weak refcount | |
7 | +-----------------+ |
8 | | Deleter | |
9 | +-----------------+ |
10 | | Allocator | |
11 | +-----------------+ |
12 +---------------------+

6.2 生命周期管理

  • 当最后一个 shared_ptr 销毁时,对象被销毁
  • 控制块会保留直到最后一个 weak_ptr 销毁

7. 最佳实践

  1. 优先使用 weak_ptr 作为观察者
  2. 及时检查 expired()lock() 结果
  3. 避免长期持有 weak_ptr 导致控制块泄漏
  4. 在性能敏感场景慎用 lock()

8. 示例代码

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <memory>
2 #include <iostream>
3
4 class Resource {
5 public:
6 Resource() { std::cout << "Resource acquired\n"; }
7 ~Resource() { std::cout << "Resource released\n"; }
8 void use() { std::cout << "Resource used\n"; }
9 };
10
11 int main() {
12 // 创建 shared_ptr
13 std::shared_ptr<Resource> sp = std::make_shared<Resource>();
14
15 // 创建 weak_ptr
16 std::weak_ptr<Resource> wp = sp;
17
18 // 使用资源
19 if (auto locked = wp.lock()) {
20 locked->use();
21 std::cout << "Use count: " << wp.use_count() << "\n";
22 }
23
24 // 释放 shared_ptr
25 sp.reset();
26
27 // 检查资源是否还存在
28 if (wp.expired()) {
29 std::cout << "Resource has been released\n";
30 }
31
32 return 0;
33 }

9. 总结

std::weak_ptr 是 C++智能指针体系中的重要组成部分,主要用于:
- 打破 shared_ptr 的循环引用
- 实现非拥有式观察
- 构建缓存等需要弱引用的场景

正确使用 weak_ptr 可以避免内存泄漏同时保持程序的灵活性。


附录: std::weak_ptr 的常用 API 表格

名称用途原型代码示例说明
构造函数创建 weak_ptr 对象weak_ptr() noexcept;
weak_ptr(const weak_ptr& x) noexcept;
weak_ptr(weak_ptr&& x) noexcept;
cpp<br>std::weak_ptr<int> w1; // 空指针<br>std::shared_ptr<int> s = std::make_shared<int>(42);<br>std::weak_ptr<int> w2(s); // 从 shared_ptr 构造<br>默认构造为空;支持拷贝/移动构造(不增加引用计数)。
析构函数释放 weak_ptr 资源~weak_ptr();(隐式调用)析构时不影响关联的 shared_ptr 引用计数。
operator=赋值操作weak_ptr& operator=(const weak_ptr& x) noexcept;
weak_ptr& operator=(weak_ptr&& x) noexcept;
cpp<br>std::weak_ptr<int> w3 = w2; // 拷贝赋值<br>std::weak_ptr<int> w4 = std::move(w3); // 移动赋值<br>赋值操作不增加引用计数,可能释放原有资源。
swap交换两个 weak_ptr 对象void swap(weak_ptr& x) noexcept;cpp<br>w1.swap(w2); // 交换 w1 和 w2<br>高效交换指针内容,不影响引用计数。
reset释放所有权并置空void reset() noexcept;cpp<br>w2.reset(); // w2 不再观测任何对象<br>weak_ptr 置为空状态,不影响关联的 shared_ptr
use_count获取关联 shared_ptr 的引用计数long use_count() const noexcept;cpp<br>if (w2.use_count() > 0) { /* ... */ }<br>返回当前观测的 shared_ptr 的引用计数(可能为 0)。
expired检查对象是否已被释放bool expired() const noexcept;cpp<br>if (!w2.expired()) { /* ... */ }<br>等价于 use_count() == 0,但更高效。
lock获取关联的 shared_ptrshared_ptr<T> lock() const noexcept;cpp<br>if (auto s = w2.lock()) { /* 安全使用 s */ }<br>若对象未释放,返回一个有效的 shared_ptr(增加引用计数);否则返回空。
owner_before提供基于所有权的排序template<class Y> bool owner_before(const shared_ptr<Y>& other) const;
template<class Y> bool owner_before(const weak_ptr<Y>& other) const;
cpp<br>std::set<std::weak_ptr<int>, std::owner_less<std::weak_ptr<int>>> s;<br>用于将 weak_ptr 作为关联容器(如 std::set)的键,基于内部控制块排序。

关键说明:

  1. 线程安全weak_ptr 的引用计数操作是原子的,但需注意竞态条件(例如 expired()lock() 之间可能有其他线程释放对象)。
  2. 循环引用weak_ptr 主要用于解决 shared_ptr 的循环引用问题(例如双向链表、观察者模式)。
  3. 性能lock() 是原子操作,可能比 expired() + lock() 组合更高效。

如果需要进一步补充或调整,请随时指出!

文章目录