Skip to content

std::weak_ptr 详解

一、概念

weak_ptr观察者:指向由 shared_ptr 管理的对象,但不增加强引用计数(不参与所有权)。用于监测对象是否存在,需要真正使用时通过 lock() 提升为 shared_ptr。

cpp
std::shared_ptr<Foo> sp = std::make_shared<Foo>();
std::weak_ptr<Foo> wp = sp;   // strong=1, weak=1
sp.reset();                   // strong=0 → 对象被删;weak 计数随之减
if (auto s = wp.lock()) {     // 提升成功(对象还活着)时才能安全使用
    s->doSomething();
} else {
    // 对象已被销毁
}

二、底层原理

weak_ptr 内部也持有指向控制块的指针,但它增加的是弱引用计数(weak count),而非强引用计数。

  • 强引用计数归零(use_count==0) → 删除对象本身。
  • 控制块不会立即释放:只有当强计数 + 弱计数都归零时,控制块才释放。
  • 原因:weak_ptr 需要访问控制块里判断对象是否还活着、去 lock;若对象删了就立即把控制块 free,weak_ptr 就成悬垂(不能 lock 也不能安全 expired)。

结论:对象的生命期由 strong 计数决定;控制块的释放由 (strong + weak) 计数决定。

三、核心方法

方法说明
lock()若 use_count>0 返回 shared_ptr(提升),否则返回空 shared_ptr。线程安全,原子
expired()等价 use_count()==0(对象已销毁)
use_count()被管理对象的强引用数
reset()放弃观测
owner_before用于做 map 的 key(提供严格弱序)

四、高频问答

Q1. weak_ptr 有什么用?经典循环引用

cpp
struct A;
struct B;
struct A { std::shared_ptr<B> b; ~A(){ std::cout<<"~A\n";} };
struct B { std::shared_ptr<A> a; ~B(){ std::cout<<"~B\n";} };

int main() {
    std::shared_ptr<A> pa = std::make_shared<A>();
    std::shared_ptr<B> pb = std::make_shared<B>();
    pa->b = pb;   // A → B
    pb->a = pa;   // B → A(形成环)
    // main 结束,pa/pb 各自离开但互相引用,计数永不为 0 → 泄漏 💥
}

解决:把其中一方的 shared_ptr 改为 weak_ptr:

cpp
struct B { std::weak_ptr<A> a; };   // B 不拥有 A,只是观测

当 pa 在 main 结束时,A 计数归零可析构;B 中 a 是 weak,不影响 A 析构,环被打破,B 正常析构。

Q2. 循环引用为什么一定会泄漏 / 在哪出问题?

环中每个对象都被环内另一个对象强引用兜住,即使外部所有权全部释放,内部引用计数仍 ≥1,析构函数永不被调用 → 内存(及析构副作用)永不释放。破解:让环中「不拥有」的一方用 weak_ptr 表达(谁是父/子所有权清晰)。

Q3. 怎么访问 weak_ptr 指向的对象?

必须 lock()(获得 shared_ptr 才能 ->)。weak_ptr 本身不支持 operator->/operator*,否则无法保证安全性。

Q4. 为什么 lock() 是线程安全的?

lock() 内部原子地判断 use_count:若 >0 则原子 +1 强计数并返回;若已为 0 返回空。这个「检查后提升」是原子的,避免「刚判断活着、下一刻被另一线程删掉再去加计数」的竞态。

Q5. weak_ptr 能直接比较 value 吗?

比较对象 (相等) 需转成 shared_ptr(若已过期无法比较值)。owner_before 用于在 std::set/std::map 中给 weak_ptr 排序(不需要对象存活,只比较所有者控制块地址)。

Q6. weak_ptr 的典型使用场景?

  1. 破除循环引用(最常见)。
  2. 缓存:缓存对象列表用 weak_ptr 监测,对象被外部删后自动失效(如资源缓存、观察者模式)。
  3. enable_shared_from_this 内部:继承该类时类内部保存一个 weak_ptr<T>(就是由创建它的 shared_ptr 初始化),成员调 shared_from_this() 时对该 weak_ptr 做 lock()

五、记忆口诀

  • strong 计数为 0 → 对象销毁;
  • strong + weak 都为 0 → 控制块销毁;
  • 想看但不想拥有 → weak_ptr + lock()
  • 环上某向改用 weak_ptr → 打破循环。

C++ 面试八股 · VitePress 版