智能指针循环引用问题

  • Post author:
  • Post category:其他




智能指针循环引用

循环引用简单来说就是:两个对象互相使用一个

shared_ptr

成员变量指向对方,这会造成循环引用。导致引用计数失效,内存无法释放。

#include <iostream>
#include <memory>

class CB;
class CA
{
public:
	CA()
    {
		std::cout << "CA()" << std::endl;
    }
    ~CA()
    {
		std::cout << "~CA()" << std::endl;
    }
    void set_ptr(std::shared_ptr<CB>& ptr)
    {
		m_ptr_b = ptr;
    }
private:
    std::shared_ptr<CB> m_ptr_b;
};

class CB
{
public:
    CB()
    {
		std::cout << "CB()" << std::endl;
    }
    ~CB()
    {
		std::cout << "~CB()" << std::endl;
    }
    void set_ptr(std::shared_ptr<CA>& ptr)
    {
		m_ptr_a = ptr;
    }
private:
    std::shared_ptr<CA> m_ptr_a;
};

int main()
{
	std::shared_ptr<CA> ptr_a(new CA());
    std::shared_ptr<CB> ptr_b(new CB());
    
    ptr_a->set_ptr(ptr_b);
    ptr_b->set_ptr(ptr_a);
    std::cout << ptr_a.use_count() << " " << ptr_b.use_count() << std::endl;
	return 0;
}
/*
CA()
CB()
2 2
*/

既然析构函数没有调用,就说明

ptr_a



ptr_b

两个变量的引用计数都不是0。

分析一下引用情况

在这里插入图片描述

起初定义完

ptr_a



ptr_b

时,只有1,3两条引用,即

ptr_a

指向CA对象,

ptr_b

指向CB对象。然后调用函数

set_ptr()

后又增加了2,4两条引用,即CB对象中的

m_ptr_a

成员变量指向CA对象,CA对象中的

m_ptr_b

成员变量指向CB对象。

这个时候,指向CA对象的有两个,指向CB对象的也有两个。当main函数运行结束时,对象

ptr_a



ptr_b

被销毁,也就是1,3两条引用会被断开,2,4两条引用依然存在,每一个的引用计数都不为0,结果就导致其指向的内部对象无法析构,造成内存泄漏。



循环引用解决方法:weak_ptr

循环引用:对被调用的析构函数没有被调用,从而出现了内存泄露。


  • weak_ptr

    对被

    shared_ptr

    管理的对象存在

    非拥有性引用



    在访问所引用的对象前必须先转化为

    shared_ptr



  • weak_ptr

    用来打断

    shared_ptr

    所管理对象的循环引用问题,若这种环被孤立(没有指向环中的外部共享指针),

    shared_ptr

    引用计数无法抵达0,内存被泄露;令环中的指针之一为弱指针可以避免该情况。


  • weak_ptr

    用来表示临时所有权的概念。当某个对象只有存在时才需要被访问,而且随时可能被他人删除,可以用

    weak_ptr

    跟踪该对象;需要获得所有权时将其转化为

    shared_ptr

    ,此时如果原来的

    shared_ptr

    被销毁,则该对象的生命周期被延长到这个临时的

    shared_ptr

    同样被销毁。

#include <iostream>
#include <memory>

class CB;
class CA
{
public:
	CA()
    {
		std::cout << "CA()" << std::endl;
    }
    ~CA()
    {
		std::cout << "~CA()" << std::endl;
    }
    void set_ptr(std::shared_ptr<CB>& ptr)
    {
		m_ptr_b = ptr;
    }
private:
    std::shared_ptr<CB> m_ptr_b;
};

class CB
{
public:
    CB()
    {
        std::cout << "CB()" << std::endl;
    }
    ~CB()
    {
        std::cout << "~CB()" << std::endl;
    }
    void set_ptr(std::shared_ptr<CA>& ptr)
    {
        m_ptr_a = ptr;
    }
private:
    std::weak_ptr<CA> m_ptr_a;
};

int main()
{
	std::shared_ptr<CA> ptr_a(new CA());
    std::shared_ptr<CB> ptr_b(new CB());
    
    ptr_a->set_ptr(ptr_b);
    ptr_b->set_ptr(ptr_a);
    std::cout << ptr_a.use_count() << " " << ptr_b.use_count() << std::endl;
	return 0;
}
/*
CA()
CB()
1 2
~CA()
~CB()
*/

在这里插入图片描述

流程与上一例子大体相似,但是不同的是4这条引用是通过

weak_ptr

建立的,并不会增加引用计数。也就是说,CA的对象只有一个引用计数,而CB的对象只有两个引用计数,当main函数返回时,对象

ptr_a



ptr_b

被销毁,也就是1,3两条引用会被断开,此时CA对象的引用计数会减为0,对象被销毁,进而解决了引用成环的问题。