C++中的weak_ptr是一种弱引用智能指针,它不会增加所指向对象的引用计数,主要用于辅助shared_ptr工作,最核心的作用就是解决shared_ptr之间形成的循环引用问题,避免动态内存无法被正确释放。

weak_ptr的基本特性
weak_ptr不能直接用来访问对象,它必须配合shared_ptr使用,本身不会拥有对象的所有权,因此不会增加对象的引用计数。它的主要操作包括:
- 通过shared_ptr构造或赋值,观察对应对象的状态
- 调用
lock()方法获取对应的shared_ptr,如果对象还存在则返回有效指针,否则返回空指针 - 调用
expired()方法判断所观察的对象是否已经被释放 - 调用
reset()方法解除对对象的观察
循环引用是如何产生的
当两个或多个shared_ptr互相指向对方管理的对象时,就会形成循环引用。此时每个对象的引用计数都不会降到0,导致析构函数无法被调用,内存泄漏。
比如下面这个典型的循环引用场景:
#include <iostream>
#include <memory>
using namespace std;
class B; // 前向声明
class A {
public:
shared_ptr<B> b_ptr; // A持有B的shared_ptr
~A() {
cout << "A的析构函数被调用" << endl;
}
};
class B {
public:
shared_ptr<A> a_ptr; // B持有A的shared_ptr
~B() {
cout << "B的析构函数被调用" << endl;
}
};
int main() {
shared_ptr<A> a = make_shared<A>();
shared_ptr<B> b = make_shared<B>();
// 形成循环引用:a的b_ptr指向b,b的a_ptr指向a
a->b_ptr = b;
b->a_ptr = a;
return 0;
}
运行上面的代码会发现,A和B的析构函数都没有被调用,因为a和b销毁时,各自的引用计数从2降到1,不会触发对象释放,导致内存泄漏。
用weak_ptr解决循环引用
要解决循环引用,只需要把其中一个shared_ptr换成weak_ptr即可,因为weak_ptr不会增加引用计数,不会形成互相持有所有权的闭环。
修改上面的代码,把B中的a_ptr改为weak_ptr:
#include <iostream>
#include <memory>
using namespace std;
class B; // 前向声明
class A {
public:
shared_ptr<B> b_ptr; // A持有B的shared_ptr
~A() {
cout << "A的析构函数被调用" << endl;
}
};
class B {
public:
weak_ptr<A> a_ptr; // B持有A的weak_ptr,不增加引用计数
~B() {
cout << "B的析构函数被调用" << endl;
}
};
int main() {
shared_ptr<A> a = make_shared<A>();
shared_ptr<B> b = make_shared<B>();
a->b_ptr = b; // b的引用计数变为2
b->a_ptr = a; // a的引用计数保持1,因为weak_ptr不增加计数
// 程序结束时,a先销毁,引用计数从1降到0,A对象被释放,b的引用计数从2降到1
// 接着b销毁,引用计数从1降到0,B对象被释放
return 0;
}
运行修改后的代码,会正常输出A和B的析构函数调用信息,说明内存被正确释放了。
weak_ptr的使用注意事项
使用weak_ptr时需要注意以下几点:
- 不能直接用weak_ptr访问对象,必须先调用
lock()获取shared_ptr,判断返回的指针是否有效后再使用 - weak_ptr的生命周期不会影响所观察对象的生命周期,对象释放后,weak_ptr会自动变为过期状态
- 不要将weak_ptr作为函数的返回值或者长期存储的对象,它更适合作为临时观察的辅助工具
下面是使用lock()方法的正确示例:
#include <iostream>
#include <memory>
using namespace std;
int main() {
shared_ptr<int> sp = make_shared<int>(10);
weak_ptr<int> wp(sp); // wp观察sp指向的对象
if (shared_ptr<int> tmp = wp.lock()) { // 获取有效shared_ptr
cout << "对象值:" << *tmp << endl;
} else {
cout << "对象已经被释放" << endl;
}
sp.reset(); // 释放对象
if (wp.expired()) {
cout << "对象已经过期" << endl;
}
return 0;
}
weak_ptrshared_ptr循环引用智能指针修改时间:2026-07-11 18:51:24