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
    评论
shared_ptrC++中智能指针的一种,它可以对动态分配的对象进行引用计数管理,当引用计数为0时自动释放内存。它的使用场景包括但不限于以下几种: 1. 动态分配对象的管理:当需要在堆上动态分配对象时,可以使用shared_ptr来管理对象的生命周期,避免手动调用delete释放内存,从而降低内存泄漏和悬空指针等问题的风险。 2. 多个指针共享同一个资源:当多个指针需要共享同一个资源(如对象或数组)时,可以使用shared_ptr来管理这个资源的生命周期。每个shared_ptr都会维护一个计数器,记录当前有多少个shared_ptr指向该资源,当计数器变为0时,资源会被自动释放。 3. 在容器中存储动态分配的对象:在使用容器(如vector、list等)存储动态分配的对象时,如果使用原始指针,容器在元素添加、删除或者发生扩容时可能会造成内存泄漏或者悬空指针。而使用shared_ptr作为容器元素,可以确保对象在不再需要时被正确释放。 4. 实现自定义的引用计数机制:shared_ptr提供了一个方便的接口,可以方便地实现自定义的引用计数机制,例如通过使用make_shared函数创建shared_ptr实例,并自定义删除器来管理额外的资源。 总之,shared_ptr适用于需要动态分配对象且多个指针需要共享该对象的情况,它能够简化内存管理,并提供了一种安全、高效的方式来管理动态分配的资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值