weak_ptr用于解决shared_ptr的循环引用问题,示例中A和B互相持有shared_ptr导致内存泄漏,将其中一个改为weak_ptr后打破循环,引用计数正常归零,对象可析构,weak_ptr通过lock()安全访问目标,常用于父子节点等场景。
在 C++ 中,weak_ptr 主要用于解决 shared_ptr 可能引起的循环引用问题。当两个或多个对象通过 shared_ptr 相互持有对方时,引用计数永远无法归零,导致内存泄漏。weak_ptr 不增加引用计数,仅观察对象是否存在,因此适合打破这种循环。
循环引用问题示例
考虑两个类 A 和 B,它们互相持有对方的 shared_ptr:
#include <memory> #include <iostream> struct B; // 前向声明 struct A { std::shared_ptr<B> b_ptr; ~A() { std::cout << "A 被销毁n"; } }; struct B { std::shared_ptr<A> a_ptr; ~B() { std::cout << "B 被销毁n"; } }; int main() { auto a = std::make_shared<A>(); auto b = std::make_shared<B>(); a->b_ptr = b; b->a_ptr = a; std::cout << "a 引用计数: " << a.use_count() << "n"; // 输出 2 std::cout << "b 引用计数: " << b.use_count() << "n"; // 输出 2 return 0; }
程序结束时,A 和 B 的析构函数都不会被调用。因为 a 和 b 的引用计数都是 2(外部变量 + 对方持有),离开作用域后引用计数变为 1,无法释放。
使用 weak_ptr 打破循环
修改其中一个引用为 weak_ptr,可打破循环。通常“从属”或“反向”引用使用 weak_ptr。
struct A { std::shared_ptr<B> b_ptr; ~A() { std::cout << "A 被销毁n"; } }; struct B { std::weak_ptr<A> a_ptr; // 改为 weak_ptr ~B() { std::cout << "B 被销毁n"; } };
此时 main 函数中:
- a 的引用计数:1(仅由 main 中的变量持有)
- b 的引用计数:2(main 持有 + a 持有)
当 main 结束时,a 和 b 的 shared_ptr 被释放,引用计数归零,对象正常析构。
weak_ptr 的安全访问
使用 weak_ptr 时,不能直接访问对象,必须先转为 shared_ptr:
void check_A(std::weak_ptr<A> &wp) { if (auto sp = wp.lock()) { // 对象仍存在,sp 是临时 shared_ptr std::cout << "A 仍然存在n"; } else { std::cout << "A 已被释放n"; } }
lock() 返回 shared_ptr,若对象已销毁则返回空。这保证了线程安全和生命周期管理。
基本上就这些。weak_ptr 不参与所有权,只观察,是打破 shared_ptr 循环引用的标准做法。常见于父子节点、观察者模式、缓存等场景。不复杂但容易忽略,用对了能避免内存泄漏。
评论(已关闭)
评论已关闭