shared_ptr循环引用问题

shared_ptr循环引用问题

循环引用指的是两个或多个对象之间相互持有对方的引用,形成一个闭环的引用关系。换句话说,每个对象都拥有对其他对象的引用,导致这些对象的引用计数无法归零,从而无法释放它们所占用的内存。

循环引用通常发生在使用智能指针(如shared_ptr)进行内存管理的情况下。当两个或多个对象使用shared_ptr相互持有对方的指针时,每个对象的引用计数都至少为1。即使在没有外部引用的情况下,这些对象的引用计数也不会降为0,因为它们彼此之间保持着引用。

由于引用计数无法降为0,这些对象的析构函数永远不会被调用,导致资源泄漏。这是因为智能指针在引用计数归零时才会自动释放所管理的内存。循环引用阻止了引用计数归零,因此会造成内存泄漏的问题。

代码示例

#include <iostream>
#include<memory>

using namespace std;

class B;

class A
{
public:
    A()
    {
        cout<<"A() B count:"<<m_spB.use_count()<<endl;
    }
    ~A()
    {
        cout<<"~A() B count:"<<m_spB.use_count()<<endl;
    }
    shared_ptr<B> m_spB;
};

class B
{
public:
    B()
    {
        cout<<"B() A count:"<<m_spA.use_count()<<endl;
    }
    ~B()
    {
        cout<<"~B() A count:"<<m_spA.use_count()<<endl;
    }
    shared_ptr<A> m_spA;
};

int main()
{
    shared_ptr<A>spA(new A());
    shared_ptr<B>spB(new B());

    spB->m_spA = spA;
    spA->m_spB = spB;
    return 0;
}

上述代码指针指向示意图图
智能指针的互相指向

解决办法

为了解决循环引用的问题,可以使用weak_ptr来取代shared_ptr。weak_ptr允许对象之间存在引用,但不会增加引用计数,也不会阻止对象的销毁。通过将m_spA和m_spB改为weak_ptr类型,可以避免循环引用导致的内存泄漏。

修改代码如下

#include <iostream>
#include <memory>

using namespace std;

class B;

class A
{
public:
    A()
    {
        cout << "A() B count: " << m_spB.use_count() << endl;
    }
    ~A()
    {
        cout << "~A() B count: " << m_spB.use_count() << endl;
    }
    weak_ptr<B> m_spB;
};

class B
{
public:
    B()
    {
        cout << "B() A count: " << m_spA.use_count() << endl;
    }
    ~B()
    {
        cout << "~B() A count: " << m_spA.use_count() << endl;
    }
    weak_ptr<A> m_spA;
};

int main()
{
    shared_ptr<A> spA(new A());
    shared_ptr<B> spB(new B());

    spB->m_spA = spA;
    spA->m_spB = spB;

    return 0;
}

weak_ptr介绍

weak_ptr是C++标准库中智能指针的一种类型,用于解决循环引用问题和避免悬挂指针(dangling pointer)的情况发生。与shared_ptr不同,weak_ptr并不拥有所指向对象的所有权,它只是对被管理对象的一个观察者。

主要作用如下:

  1. 解决循环引用问题:当两个或多个对象相互持有对方的shared_ptr时,形成循环引用。这种情况下,对象的引用计数永远不会变为0,导致内存泄漏。通过使用weak_ptr可以打破循环引用,避免内存泄漏的发生。

代码见上面示例;

  1. 观察资源的生命周期:weak_ptr允许你观察由shared_ptr管理的资源的生命周期,而不会延长资源的生命周期。你可以通过expired()函数检查所观察的对象是否已经被释放。
    代码举例
#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() {
        std::cout << "MyClass Constructor" << std::endl;
    }
    ~MyClass() {
        std::cout << "MyClass Destructor" << std::endl;
    }
};

int main() {
    std::shared_ptr<MyClass> sharedPtr(new MyClass());
    std::weak_ptr<MyClass> weakPtr(sharedPtr);

    if (auto lockedPtr = weakPtr.lock()) {
        std::cout << "Object is alive" << std::endl;
    } else {
        std::cout << "Object has been destroyed" << std::endl;
    }

    // 销毁 sharedPtr,对象被释放
    sharedPtr.reset();

    if (auto lockedPtr = weakPtr.lock()) {
        std::cout << "Object is alive" << std::endl;
    } else {
        std::cout << "Object has been destroyed" << std::endl;
    }

    return 0;
}

在这个例子中,我们创建了一个名为MyClass的类,并使用shared_ptr进行内存管理。然后,将该shared_ptr转换为weak_ptr。
首先,我们通过调用lock()函数创建了一个新的shared_ptr,命名为lockedPtr。在这种情况下,由于对象存在,lock()函数返回一个有效的shared_ptr,因此输出Object is alive。
接下来,我们通过调用sharedPtr.reset()销毁了shared_ptr,从而释放了所管理的对象。此时,weak_ptr指向的对象已经被销毁。
再次调用lock()函数创建新的shared_ptr,此时由于对象已经被销毁,lock()函数将返回一个空的shared_ptr。因此,输出Object has been destroyed。
通过使用lock()函数和条件语句,我们可以观察由shared_ptr管理的资源是否仍然存在,从而确定对象的生命周期状态。如果lock()函数返回一个有效的shared_ptr,表示对象仍然存活;如果返回一个空的shared_ptr,表示对象已经被销毁。这样可以安全地观察对象的生命周期并采取相应的操作。

  1. 安全地访问对象:通过调用lock()函数,可以从weak_ptr获得一个有效的shared_ptr来安全地访问所指向的对象。如果对象仍然存在,则lock()函数返回一个有效的shared_ptr;如果对象已经被销毁,则返回一个空的shared_ptr
    代码举例
#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() {
        std::cout << "MyClass Constructor" << std::endl;
    }
    ~MyClass() {
        std::cout << "MyClass Destructor" << std::endl;
    }
    void SomeFunction() {
        std::cout << "SomeFunction called" << std::endl;
    }
};

int main() {
    std::shared_ptr<MyClass> sharedPtr(new MyClass());
    std::weak_ptr<MyClass> weakPtr(sharedPtr);

    sharedPtr->SomeFunction();  // 正常调用

    // 销毁 sharedPtr,对象被释放
    sharedPtr.reset();

    std::shared_ptr<MyClass> newSharedPtr = weakPtr.lock();
    if (newSharedPtr) {
        newSharedPtr->SomeFunction();  // 安全调用,对象存在
    } else {
        std::cout << "Object has been destroyed" << std::endl;  // 对象已经被销毁
    }

    return 0;
}
  1. 防止悬挂指针(dangling pointer):当你使用shared_ptr来管理对象,并且在某处需要引用该对象但不拥有所有权时,可以使用weak_ptr。这样可以避免悬挂指针的情况发生,即在对象已经被销毁后仍然持有指向它的指针。

代码见上面示例;

总之,weak_ptr提供了一种非拥有性的、观察所管理对象生命周期的方式,能够解决循环引用和悬挂指针等问题,增加了智能指针的灵活性和安全性。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
shared_ptr循环引用是指多个shared_ptr实例互相引用,导致对象无法被释放的情况。shared_ptr采用引用计数的智能指针,可以指向同一个动态对象,并维护了一个共享的引用计数器。当多个shared_ptr实例相互引用时,它们的引用计数无法降为零,从而导致内存泄漏。 为了解决shared_ptr引起的循环引用问题,可以使用weak_ptr指针。weak_ptr是一种弱引用,不能单独使用,只能配合shared_ptr使用。相比之下,weak_ptr并不增加引用计数,它只是提供了对被共享对象的一个非拥有的引用。通过使用weak_ptr,可以打破shared_ptr之间的循环引用,使对象能够正常释放。 在C++中,我们可以使用weak_ptr的lock()函数来获取一个可用的shared_ptr对象,从而访问被共享对象的成员函数和成员变量。weak_ptr没有重载*和->运算符,因此无法直接访问对象,但可以使用lock()函数来获取shared_ptr,并通过该shared_ptr来访问对象的成员。 通过使用weak_ptr解决shared_ptr循环引用问题,可以避免内存泄漏和资源占用过多的情况。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++11智能指针之weak_ptr详解](https://download.csdn.net/download/weixin_38740201/14841441)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [C++ 智能指针的循环引用问题](https://blog.csdn.net/qq_28584889/article/details/88726324)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [C++智能指针(share_ptr)及其循环引用问题](https://blog.csdn.net/feikudai8460/article/details/104747490)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值