C++11新特性_标准库_智能指针_std::weak_ptr
在 C++ 中,std::shared_ptr
通过引用计数实现了共享所有权,但如果存在循环引用(两个对象通过 std::shared_ptr
互相指向对方),会导致引用计数无法归零,最终引发内存泄漏。std::weak_ptr
是一种 “弱引用” 智能指针,不参与引用计数,专门用于解决循环引用问题。
循环引用的问题
当两个对象通过 std::shared_ptr
互相引用时,会形成循环依赖:每个对象的引用计数被对方的 std::shared_ptr
持握,即使外部不再有任何 std::shared_ptr
指向它们,双方的引用计数仍无法减至 0,导致对象无法被销毁,内存泄漏。
循环引用示例(内存泄漏)
#include <iostream>
#include <memory>class B; // 前置声明class A {
public:std::shared_ptr<B> b_ptr; // A 持有 B 的共享指针~A() { std::cout << "A 析构" << std::endl; }
};class B {
public:std::shared_ptr<A> a_ptr; // B 持有 A 的共享指针~B() { std::cout << "B 析构" << std::endl; }
};int main() {auto a = std::make_shared<A>(); // a 的引用计数 = 1auto b = std::make_shared<B>(); // b 的引用计数 = 1a->b_ptr = b; // b 的引用计数 +=1 → 2b->a_ptr = a; // a 的引用计数 +=1 → 2// 此时,a 和 b 的引用计数均为 2// 当 main 函数结束时,a 和 b 离开作用域,引用计数减为 1(被对方的 shared_ptr 持有)// 由于引用计数无法归零,A 和 B 的析构函数不会被调用,内存泄漏!return 0;
}
运行结果:
终端不会输出 A 析构
和 B 析构
,说明对象未被销毁。
std::weak_ptr
解决循环引用
std::weak_ptr
是一种弱引用指针,不参与引用计数,仅用于观察 std::shared_ptr
管理的对象。它的核心作用是:
- 指向
std::shared_ptr
管理的对象,但不会增加引用计数; - 可以通过
lock()
方法获取std::shared_ptr
(若对象存活),确保安全访问; - 配合
expired()
方法判断对象是否已被销毁。
用 std::weak_ptr
打破循环引用
修改上述示例,将其中一个类的成员变量改为 std::weak_ptr
,即可打破循环:
#include <iostream>
#include <memory>class B; // 前置声明class A {
public:std::shared_ptr<B> b_ptr; // A 仍用 shared_ptr 持有 B(假设 A 需要“强”拥有 B)~A() { std::cout << "A 析构" << std::endl; }
};class B {
public:std::weak_ptr<A> a_ptr; // B 用 weak_ptr 观察 A(弱引用,不参与计数)~B() { std::cout << "B 析构" << std::endl; }
};int main() {auto a = std::make_shared<A>(); // a 的引用计数 = 1auto b = std::make_shared<B>(); // b 的引用计数 = 1a->b_ptr = b; // b 的引用计数 +=1 → 2b->a_ptr = a; // a 的引用计数 **不变化**(weak_ptr 不增加计数)// 当 main 函数结束时:// - a 离开作用域,引用计数减为 0(因为 b->a_ptr 是 weak_ptr,不持有计数)// → A 对象被销毁,触发 A 的析构函数;// - a->b_ptr 被销毁,b 的引用计数减为 1(原计数 2 → 1);// - b 离开作用域,引用计数减为 0 → B 对象被销毁,触发 B 的析构函数。return 0;
}
运行结果:
A 析构
B 析构
关键机制总结
-
std::weak_ptr
不参与引用计数:
当B
的成员a_ptr
是std::weak_ptr
时,它不会增加A
对象的引用计数。因此,当外部a
(std::shared_ptr<A>
)离开作用域时,A
的引用计数减为 0,触发A
的析构,进而释放A
持有的B
的std::shared_ptr
(a->b_ptr
),最终B
的引用计数也减为 0,触发B
的析构。 -
安全访问对象:
若B
需要访问A
对象,需通过std::weak_ptr::lock()
方法获取std::shared_ptr
,确保对象存活:void B::doSomething() {if (auto a = a_ptr.lock()) { // 若 A 对象存活,lock() 返回有效的 shared_ptr// 安全访问 a 的成员} else {// A 已销毁,处理空指针逻辑} }
-
判断对象存活:
通过std::weak_ptr::expired()
方法可判断对象是否已被销毁(即引用计数是否为 0):if (!a_ptr.expired()) {// 对象存活 }
总结
std::weak_ptr
通过弱引用机制(不参与引用计数)打破循环引用,确保对象在无外部强引用时能被正确销毁。它通常用于以下场景:
- 解决
std::shared_ptr
的循环引用问题; - 缓存或观察者模式中,需要观察对象但不影响其生命周期;
- 避免因强引用导致的对象无法析构。