【C++11】C++智能指针循环引用问题分析

C++11中引入了三种智能指针,分别是shared_ptr、weak_ptr和unique_ptr

智能指针的作用

智能指针可以帮助我们管理动态分配的堆内存,减少内存泄漏的可能性
手动管理堆内存有引起内存泄漏的可能,比如这段代码

try {
    int* p = new int;
    // Do something
    delete p;
} catch(...) {
    // Catch exception
}

如果在执行Do something的时候发生了异常,那么程序就会直接跳到catch语句捕获异常,delete p这句代码不会被执行,发生了内存泄漏
我们把上面的程序改成

try {
    shared_ptr<int> p(new int);
    // Do something
} catch(...) {
    // Catch exception
}

当执行Do something的时候发生了异常,那么try块中的栈对象都会被析构。因此代码中p的析构函数会被调用,引用计数从1变成0,通过new分配的堆内存被释放,这样就避免了内存泄漏的问题

循环引用问题

虽然智能指针会减少内存泄漏的可能性,但是如果使用智能指针的方式不对,一样会造成内存泄漏。比较典型的情况是循环引用问题,比如这段代码

class B; // 前置声明
class A {
public:
    shared_ptr<B> ptr;
};

class B {
public:
    shared_ptr<A> ptr;
};

int main()
{
    while(true) {
        shared_ptr<A> pa(new A());
        shared_ptr<B> pb(new B());
        pa -> ptr = pb;
        pb -> ptr = pa;
    }
    return 0;
}

这个程序中智能指针的引用情况如下图

在这里插入图片描述

上图中,class A和class B的对象各自被两个智能指针管理,也就是pa和pb引用计数都为2,为什么是2?

因为shared_ptr是一组指针指向一个实例,所以有几个shared_ptr指向类A,则类A就有几次引用。,class B对象同理。

在这种情况下,在main函数中一个while循环结束的时候,pa和pb的析构函数被调用,但是class A对象和class B对象仍然被一个智能指针管理,pa和pb引用计数变成1,于是这两个对象的内存无法被释放,造成内存泄漏,如下图所示

在这里插入图片描述

解决方法weak_ptr

std::weak_ptr

避免shared_ptr内存泄漏的利器。👈


smart pointer 三兄弟性格各异。unque_ptr是独来独往,shared_ptr是左拥右抱,而weak_ptr生来就不是为了单打独斗,了解之后你会发现他总是和shared_ptr出双入对。

weak_ptr为什么存在?

特性:

不可使用* 和 ->访问对象

被赋值,不会引起shared_ptr内部计数器值变化(我猜是它严格上来说不具备指针的能力---访问对象)

 

 所以,我们就可以用weak_ptr替代shared_ptr, 看👇例子。

#include <iostream>
#include <memory>
using namespace std;

class B;
class A
{
public:
    A()
    {
        cout << "A's constructor ..." << endl;
    }
    ~A()
    {
        cout << "A's destructor ..." << endl;
    }

    std::weak_ptr<B> weak_b;
};

class B
{
public:
    B()
    {
        cout << "B's constructor ..." << endl;
    }
    ~B()
    {
        cout << "B's destructor ..." << endl;
    }

    std::weak_ptr<A> weak_a;
};

int main()
{
    std::shared_ptr<A> aa = make_shared<A>(); //aa->object A aa计数器 1
    std::shared_ptr<B> bb = make_shared<B>(); //bb->object B bb计数器 1

    aa->weak_b = bb; //计数器还是1哦
    bb->weak_a = aa; //计数器还是1哦

    return 0;
}

 

object A & B都被成功析构(销毁)了。那么循环引用导致shared_ptr发生内存泄漏的问题迎刃而解!

原因是:weak_ptr不会增加shared_ptr的计数器,从而离开mian函数作用域时,shared_ptr aa & bb 计数器都 -1 ,成为0, 具备销毁条件,调用析构函数销毁自己和所指对象

至此,我们终于可以说weak_ptr具备避免内存泄漏的功能了!!!

https://www.cnblogs.com/yocichen/p/10563124.html

posted on 2022-10-04 01:26  bdy  阅读(100)  评论(0编辑  收藏  举报

导航