使用 shared_ptr 可以帮助管理动态分配的内存,它使用引用计数的方式来跟踪共享对象的引用数量,当引用计数为零时,会自动释放内存。然而,shared_ptr 也存在一些潜在的内存泄漏的场景,下面是一些常见的情况:
一、循环引用
当两个或多个 shared_ptr 相互引用形成循环时,引用计数永远无法降为零,导致内存泄漏。这种情况通常发生在对象之间存在相互引用的情况下。
class Node {
public:
std::shared_ptr<Node> next;
};
int main() {
std::shared_ptr<Node> node1(new Node);
std::shared_ptr<Node> node2(new Node);
node1->next = node2;
node2->next = node1;
return 0;
}
在此示例中,node1 和 node2 彼此引用,形成了一个循环引用,因此它们的内存将不会被释放。
class A {
public:
std::shared_ptr<B> b;
};
class B {
public:
std::shared_ptr<A> a;
};
int main() {
std::shared_ptr<A> a(new A);
std::shared_ptr<B> b(new B);
a->b = b;
b->a = a;
return 0;
}
在此示例中,对象A和B之间存在相互引用,因此它们的内存将不会被释放。
解决方案,使用weak_ptr
class A;
class B {
public:
std::weak_ptr<A> a;
};
class A {
public:
std::shared_ptr<B> b;
};
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->b = b;
b->a = a;
在上面的例子中,B
持有 A
的 weak_ptr
,而 A
持有 B
的 shared_ptr
,这样即使存在循环引用,当只剩下 weak_ptr
时,引用计数可以归零,从而释放内存。
二、 函数间传递shared_ptr
当shared_ptr作为函数参数传递,可能会出现内存泄漏。如果函数将shared_ptr存储在一个对象中,而该对象的生命周期比shared_ptr更长,则会出现内存泄漏。为了避免这种情况,可以使用weak_ptr或者std::move将shared_ptr的所有权转移给该函数。
三、多线程
如果多个线程同时访问同一个智能指针,而没有适当的同步机制,就可能导致竞态条件和数据竞争。这可能会导致内存访问冲突和未定义行为。
四、裸指针与智能指针混用
如果使用智能指针时,还同时使用了裸指针,并且没有将裸指针分配给智能指针或及时释放裸指针,就可能导致内存泄漏。智能指针的作用是自动管理内存,但如果同时使用了裸指针,需要确保将裸指针正确地分配给智能指针或者及时释放裸指针。
#include <iostream>
#include <memory>
using namespace std;
void proc(shared_ptr<int> value)
{
}
int main()
{
int*p = new int(100); // 裸指针
// proc(p); // 语法错, int*p 不能转换成shared_ptr<int>
shared_ptr<int> p2(p);
proc(p2);
proc(shared_ptr<int>(p));
std::cout << "*p= : " << *p << std::endl; // 潜在的不可预料的问题;因为p指向的内存已经被释放了
return 0;
}
打印
五、异常处理不当
如果在使用智能指针的过程中发生了异常,并且没有正确处理异常,可能会导致内存泄漏。例如,在使用 new
进行内存分配后,如果在后续代码中发生了异常,没有释放内存或没有将内存分配给智能指针,就会导致内存泄漏 。
问题代码
//header file
void func( shared_ptr<T1> ptr1, shared ptr<T2> ptr2 );
//call func like this
func( shared_ptr<T1>( new T1() ), shared_ptr<T2>( new T2() ) );
建议代码
//header file
void func( shared_ptr<T1> ptr1, shared_ptr<T2> ptr2 );
//call func like this
shared_ptr<T1> ptr1( new T1() );
shared_ptr<T2> ptr2( new T2() );
func(ptr1, ptr2);
六、 管理数组
使用 shared_ptr 对象管理数组时,必须使用特殊的删除器(deleter)来释放内存。
int main() {
std::shared_ptr<int> arr(new int[10], std::default_delete<int[]>());
return 0;
}
在此示例中,必须使用 std::default_delete<int[]>() 作为删除器来释放数组内存,否则将会导致内存泄漏。
参考:
C++11:再谈shared_ptr,以及shared_ptr参数传递以及构造细节_shared_ptr 多个参数_zzhongcy的博客-CSDN博客
shared_ptr使用场景、陷阱、性能分析,使用建议_INGNIGHT的博客-CSDN博客