目录
一、循环引用(最常见场景)
示例代码
内存泄漏原因
示例代码
内存泄漏(或双重释放)原因
三、解决方案
1. 循环引用:使用 std::weak_ptr
四、其他潜在风险
2. 数组管理
总结
std::shared_ptr
是 C++ 中用于管理动态内存的智能指针,通过引用计数机制自动释放对象。但在某些场景下,它仍可能导致内存泄漏。以下通过具体例子说明:
一、循环引用(最常见场景)
示例代码
#include <iostream>
#include <memory>class B; // 前向声明class A {
public:std::shared_ptr<B> b_ptr;~A() { std::cout << "A destroyed" << std::endl; }
};class B {
public:std::shared_ptr<A> a_ptr;~B() { std::cout << "B destroyed" << std::endl; }
};int main() {auto a = std::make_shared<A>(); // a的引用计数为1auto b = std::make_shared<B>(); // b的引用计数为1a->b_ptr = b; // b的引用计数变为2b->a_ptr = a; // a的引用计数变为2// main函数结束时:// a和b的引用计数减1,但仍为1(互相持有对方的shared_ptr)// 导致两者的析构函数都不会被调用,内存泄漏!return 0;
}
内存泄漏原因
a
和b
互相持有对方的shared_ptr
,形成循环引用。- 当
main
函数结束时,a
和b
的局部变量被销毁,引用计数减 1,但仍为 1(因为对方的成员变量还持有自己的指针)。 - 引用计数永远无法降为 0,导致析构函数不会被调用,内存无法释放。
二、共享指针管理的对象包含自身的 shared_ptr
示例代码
#include <iostream>
#include <memory>class Bad {
public:std::shared_ptr<Bad> getSelf() {return std::shared_ptr<Bad>(this); // 危险!}~Bad() { std::cout << "Bad destroyed" << std::endl; }
};int main() {auto b1 = std::make_shared<Bad>();auto b2 = b1->getSelf(); // 创建了第二个独立的shared_ptr管理同一对象// b1和b2的引用计数均为1// main函数结束时,b1和b2分别析构,导致对象被delete两次(双重释放)return 0;
}
内存泄漏(或双重释放)原因
getSelf()
方法中使用this
指针创建了一个新的shared_ptr
,与原shared_ptr
无关。- 同一对象被两个独立的
shared_ptr
管理,引用计数各自为 1。 - 当两个
shared_ptr
析构时,对象被重复释放,导致未定义行为(通常是程序崩溃)。
三、解决方案
1. 循环引用:使用 std::weak_ptr
std::weak_ptr
是一种弱引用,不增加引用计数,用于打破循环:
class A {
public:std::weak_ptr<B> b_ptr; // 改为weak_ptr~A() { std::cout << "A destroyed" << std::endl; }
};class B {
public:std::weak_ptr<A> a_ptr; // 改为weak_ptr~B() { std::cout << "B destroyed" << std::endl; }
};
weak_ptr
不会增加引用计数,当a
和b
局部变量销毁时,引用计数降为 0,对象正常释放。
2. 对象获取自身的 shared_ptr
:继承 std::enable_shared_from_this
#include <memory>class Good : public std::enable_shared_from_this<Good> {
public:std::shared_ptr<Good> getSelf() {return shared_from_this(); // 安全!}~Good() { std::cout << "Good destroyed" << std::endl; }
};
shared_from_this()
返回一个与已有shared_ptr
共享引用计数的新指针,避免双重释放。
四、其他潜在风险
1. 混合使用原始指针和 shared_ptr
int* raw = new int(42);
std::shared_ptr<int> ptr1(raw);
std::shared_ptr<int> ptr2(raw); // 错误!两个独立的shared_ptr管理同一内存
- 同一原始指针被多个
shared_ptr
独立管理,导致双重释放。
2. 数组管理
std::shared_ptr
默认使用 delete
释放对象,若管理数组需自定义删除器:
std::shared_ptr<int[]> arr(new int[10]); // 错误!默认使用delete而非delete[]
std::shared_ptr<int> arr(new int[10], [](int* p) { delete[] p; }); // 正确
总结
std::shared_ptr
内存泄漏的核心原因是引用计数无法降为 0,常见于循环引用和错误的指针管理。使用 std::weak_ptr
和 std::enable_shared_from_this
可有效避免这些问题。在实际开发中,应尽量避免手动管理原始指针,确保所有动态内存都由智能指针统一管理。