shared_ptr产生内存泄漏的场景

使用 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博客

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值