循环引用指两个对象的shared_ptr相互持有,导致引用计数无法归零而内存泄漏;weak_ptr不增加引用计数,可打破循环,通过lock()安全访问对象,常用于父子关系或双向链表中避免内存泄漏。

在C++中,shared_ptr通过引用计数管理对象生命周期,但当两个或多个对象互相持有对方的shared_ptr时,会形成循环引用,导致内存无法释放。这时就需要用到weak_ptr来打破循环。
什么是循环引用?
考虑两个类A和B,每个类都持有一个指向对方的shared_ptr:
struct B; // 前向声明
struct A {
std::shared_ptr ptr;
~A() { std::cout << "A destroyed\n"; }
};
struct B {
std::shared_ptr ptr;
~B() { std::cout << "B destroyed\n"; }
};
如果这样使用:
auto a = std::make_shared(); auto b = std::make_shared(); a->ptr = b; b->ptr = a;
此时a和b的引用计数都是2。离开作用域后,shared_ptr会减少引用计数到1,但由于仍大于0,析构函数不会被调用,造成内存泄漏。
立即学习“C++免费学习笔记(深入)”;
weak_ptr如何解决循环引用?
weak_ptr是shared_ptr的观察者,它不增加引用计数。它可以指向一个由shared_ptr管理的对象,但不会阻止对象被销毁。
修改上面的例子,把其中一个shared_ptr换成weak_ptr:
酷纬企业网站管理系统Kuwebs是酷纬信息开发的为企业网站提供解决方案而开发的营销型网站系统。在线留言模块、常见问题模块、友情链接模块。前台采用DIV+CSS,遵循SEO标准。 1.支持中文、英文两种版本,后台可以在不同的环境下编辑中英文。 3.程序和界面分离,提供通用的PHP标准语法字段供前台调用,可以为不同的页面设置不同的风格。 5.支持google地图生成、自定义标题、自定义关键词、自定义描
struct B;
struct A {
std::shared_ptr ptr;
~A() { std::cout << "A destroyed\n"; }
};
struct B {
std::weak_ptr ptr; // 改为 weak_ptr
~B() { std::cout << "B destroyed\n"; }
};
现在即使相互引用,也不会形成循环。当外部的shared_ptr离开作用域,引用计数正确归零,对象能被正常释放。
如何安全使用weak_ptr?
由于weak_ptr不保证所指对象一定存在,访问前必须检查:
- 使用 lock() 获取临时的shared_ptr,若对象已销毁则返回空
- 使用 expired() 判断对象是否已被释放(但有竞态风险)
推荐方式:
std::shared_ptr temp = b.ptr.lock(); if (temp) { // 安全使用 temp std::cout << "Object is alive\n"; } else { std::cout << "Object has been destroyed\n"; }
这样做既打破了循环引用,又能安全地访问目标对象。
基本上就这些。在设计有父子关系、双向链表或观察者模式等结构时,记得让从属方使用weak_ptr,主导方使用shared_ptr,就能有效避免内存泄漏。









