shared_ptr循环引用会导致内存泄漏,需用weak_ptr打破引用链;weak_ptr不增加引用计数,访问前须调用lock()获取临时shared_ptr,且应避免滥用以规避设计缺陷。

shared_ptr 循环引用会导致内存泄漏
当两个对象用 shared_ptr 互相持有对方时,引用计数永远无法归零,析构函数不会被调用,内存就一直卡着不释放。这不是“程序崩溃”,而是静默的资源泄露——跑得越久,占用越多。
用 weak_ptr 打破循环中的一个引用链
weak_ptr 不增加引用计数,只“观察”所指向的 shared_ptr 是否还有效。它本身不参与所有权管理,因此不会延长对象生命周期。
典型做法是:一方用 shared_ptr 持有另一方,另一方用 weak_ptr 回指——比如父类持子类的 shared_ptr,子类用 weak_ptr 指向父类。
- 必须在访问前调用
lock()转成临时shared_ptr,否则可能访问已销毁对象 -
weak_ptr不能直接解引用,也不能参与构造/赋值shared_ptr - 不要长期保存
weak_ptr::lock()返回的shared_ptr,除非你真需要延长其生命周期
实际代码中怎么写才安全
下面是一个父子结构的最小可运行示例,展示如何避免循环引用:
立即学习“C++免费学习笔记(深入)”;
#include <memory>
#include <iostream>
struct Child;
struct Parent {
std::shared_ptr<Child> child;
~Parent() { std::cout << "Parent destroyed\n"; }
};
struct Child {
std::weak_ptr<Parent> parent; // 注意:不是 shared_ptr<Parent>
~Child() { std::cout << "Child destroyed\n"; }
};
int main() {
auto p = std::make_shared<Parent>();
auto c = std::make_shared<Child>();
p->child = c;
c->parent = p; // 只是观察,不增加引用计数
// 安全访问父对象
if (auto locked_parent = c->parent.lock()) {
std::cout << "Parent still alive\n";
} else {
std::cout << "Parent already gone\n";
}
} // 输出:Child destroyed → Parent destroyed(顺序取决于析构时机)
weak_ptr 不是万能的,别滥用
weak_ptr 解决的是“谁该拥有谁”的设计问题,不是补丁工具。如果发现要到处加 weak_ptr,大概率是类职责或生命周期设计不合理。
常见误用包括:
- 把
weak_ptr当成“可空指针”替代shared_ptr<t></t>或裸指针——它开销更大,且每次访问都要lock() - 在回调、信号槽等跨线程场景中没考虑
weak_ptr::lock()的线程安全性(它是线程安全的,但后续使用仍需同步) - 忘记检查
lock()返回是否为空,直接解引用导致未定义行为
真正关键的,是画清楚对象图、标出所有权方向,再决定哪里该用 shared_ptr、哪里该用 weak_ptr,或者干脆换用裸指针 + 明确生命周期约束。











