八叶一刀·无仞剑

万物流转,无中生有,有归于无

导航

C++的weak_ptr

Posted on 2021-03-16 21:25  闪之剑圣  阅读(247)  评论(0编辑  收藏  举报

在以前的文章中,我们讲过C++的shared_ptr,利用它可以实现基于引用计数的指针回收,从而防止出现内存泄露。
但是事实上,即使是采用了shared_ptr,在存在循环引用的情况下其实仍然有可能会导致内存泄露,举个例子:

struct B;

struct A
{
    std::shared_ptr<B> p;
};

struct B
{
    std::shared_ptr<A> p;
};

int main()
{
    {
        auto a = std::make_shared<A>();
        auto b = std::make_shared<B>();
        a->p = b;
        b->p = a;
    }
    return 0;
}

A和B之间相互引用,在大括号中,它们的引用计数为2;即使是被销毁后,引用计数也只是变成了1,不会销毁,这样就出现了内存泄漏。
为了解决这种循环引用的问题,就需要一种特殊的智能指针:weak_ptr。这种智能指针在指向对象时,对象的引用计数不会+1,所以可以这么实现:

#include <memory>

struct B;

struct A
{
    std::weak_ptr<B> p;
};

struct B
{
    std::weak_ptr<A> p;
};

int main()
{
    {
        auto a = std::make_shared<A>();
        auto b = std::make_shared<B>();
        a->p = b;
        b->p = a;
    }
    return 0;
}

以上的代码,在大括号内,a和b的引用计数不会因为被weak_ptr指向而增加,因此它们的引用计数始终为1;这样当大括号结束的时候,它们就能被顺利销毁了。我们可以加一个变量查看一下大括号结束时的引用计数:

int main()
{
    std::weak_ptr<A> p;
    {
        auto a = std::make_shared<A>();
        auto b = std::make_shared<B>();
        a->p = b;
        b->p = a;
        p = a;
    }
    std::cout << p.use_count() << std::endl;
    return 0;
}

最终的运行结果为0,也就是此时a指向的对象已经被销毁了,不再存在内存泄漏。