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?

分析class A对象的引用情况,该对象被main函数中的pa和class B对象中的ptr管理,因此pa引用计数是2,class B对象同理。

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

在这里插入图片描述

解决方法

解决方法很简单,把class A或者class B中的shared_ptr改成weak_ptr即可,由于weak_ptr不会增加shared_ptr的引用计数,所以pa和pb中有一个的引用计数为1,在pa和pb析构时,会正确地释放掉内存

猜你喜欢

转载自blog.csdn.net/zhwenx3/article/details/82789537