题目
智能指针的循环引用问题与解决方案
信息
- 类型:问答
- 难度:⭐⭐
考点
智能指针原理,循环引用分析,weak_ptr使用,资源管理
快速回答
当两个shared_ptr相互引用时会导致循环引用问题,使引用计数无法归零,从而引发内存泄漏。解决方案:
- 使用
weak_ptr替代其中一个shared_ptr打破循环 weak_ptr不增加引用计数,通过lock()方法安全访问对象- 在存在双向关联的类关系中优先使用
weak_ptr
原理说明
智能指针通过引用计数管理对象生命周期:
shared_ptr:共享所有权,引用计数增/减1weak_ptr:观察者模式,不增加引用计数- 循环引用:当两个对象相互持有对方的
shared_ptr时,引用计数永不归零
代码示例
问题代码(内存泄漏):
class Child;
class Parent {
public:
shared_ptr<Child> child;
};
class Child {
public:
shared_ptr<Parent> parent; // 错误:导致循环引用
};
void createLeak() {
auto p = make_shared<Parent>();
auto c = make_shared<Child>();
p->child = c;
c->parent = p; // 引用计数永远≥1
} // 离开作用域后对象未被释放解决方案:
class Child {
public:
weak_ptr<Parent> parent; // 关键修改
};
void safeUsage() {
auto p = make_shared<Parent>();
auto c = make_shared<Child>();
p->child = c;
c->parent = p; // weak_ptr不增加计数
// 安全访问对象
if (auto parentPtr = c->parent.lock()) {
parentPtr->doSomething();
}
} // 对象正确释放最佳实践
- 在可能形成循环引用的场景中,将其中一个指针设为
weak_ptr - 使用
make_shared代替new创建对象(更高效且异常安全) - 访问
weak_ptr时必须检查lock()返回的shared_ptr是否为空 - 在面向对象设计中:
- 父对象持有子对象的
shared_ptr - 子对象持有父对象的
weak_ptr
- 父对象持有子对象的
常见错误
- 直接存储原始指针(
Parent*)替代weak_ptr,可能导致悬垂指针 - 忘记检查
lock()结果直接使用(对象可能已被释放) - 在类内部使用
shared_from_this()时未继承enable_shared_from_this - 在多线程环境中不加锁访问
weak_ptr
扩展知识
- enable_shared_from_this:当类需要将自身作为智能指针传递时使用
class SelfAware : public enable_shared_from_this<SelfAware> { void registerSelf() { registry.add(shared_from_this()); } }; - weak_ptr实现原理:通过控制块(control block)的弱引用计数管理,不影响对象生命周期
- 性能考量:
weak_ptr::lock()是原子操作,比shared_ptr拷贝开销小 - 替代方案:对于明确所有权的场景,优先使用
unique_ptr+原始指针观察