C++中的共享指针以及循环引用

C++ 中 shared_ptr 对象之间可以共享对象的拥有权,但是这种共享的对象引用在某些情况下可能会引发一些问题。例如,循环引用会造成两个对象之间相互引用,无法删除对象。

一个循环引用的例子

class bar;

class foo
{
public:
    
    foo()
    {

    }
    
    std::shared_ptr<bar>  ptr_bar;    
        
    ~foo()
    {
        std::cout << "foo destructed"<<std::endl;
    }
};

class bar
{
public:
    
    bar()
    {

    }
    
    std::shared_ptr<foo> ptr_foo;

    ~bar()
    {
        std::cout << "bar destructed" << std::endl;
    }
};


int main(int argc,const char* argv[])
{
    std::shared_ptr<foo> f = std::make_shared<foo>();
    std::shared_ptr<bar> b = std::make_shared<bar>();
    f->ptr_bar = b;
    b->ptr_foo = f;
    std::cout << "foo's count is " << f.use_count() << std::endl;
    std::cout << "bar's count is " << b.use_count() << std::endl;
}

//output
//foo's count is 2
//bar's count is 2

可以看到,并没有出现析构器中应该打印的信息,这说明我们构造的对象并没有被析构,那么内存便泄漏了。那么造成该现象的原因是什么呢?

std::shared_ptr 使用了引用计数算法,每当一个 std::shared_ptr 对象引用一个对象,那么会在对应的控制块中将引用计数加一,当 std::shared_ptr 对象被析构时,引用计数会减一。如果计数等于零,那么将会删除该对象。

在 main 函数里面,我们通过 std::make_shared 函数分别构造了 std::shared_ptr 和 std::shared_ptr 对象,此时 foo 对象和 bar 对象的引用计数都为 1,接着我们通过拷贝赋值运算符,将 b 和 f 分别都赋值给了 foo 对象和 bar 对象的 ptr_bar 和 ptr_foo 数据成员。std::shared_ptr 的拷贝赋值运算符的含义是共享对象的拥有权,所以,此时 foo 和 bar 对象的引用计数都为 2,这也就是打印出来的计数为 2 的原因。

当退出 main 函数的时候,f 和 b 对象都会析构。f 和 b 对象析构时都会将引用计数减一。这里就是问题所在,此时 foo 对象和 bar 对象的引用计数为一,无法达到零,导致 foo 对象和 bar 对象永远无法被删除。

利用 std::weak_ptr 打破循环引用

上述的循环引用可以通过std::weak_ptr来解决。修改的代码如下

class bar;


class foo
{
public:
    
    foo()
    {

    }
    
    std::weak_ptr<bar>  ptr_bar;    
    
    ~foo()
    {
        std::cout << "foo destructed"<<std::endl;
    }
};

class bar
{
public:
    
    bar()
    {

    }
    
    std::weak_ptr<foo> ptr_foo;

    ~bar()
    {
        std::cout << "bar destructed" << std::endl;
    }
};

int main(int argc,const char* argv[])
{
    std::shared_ptr<foo> f = std::make_shared<foo>();
    std::shared_ptr<bar> b = std::make_shared<bar>();

    f->ptr_bar = b;
    b->ptr_foo = f;
    std::cout << "foo's count is " << f.use_count() << std::endl;
    std::cout << "bar's count is " << b.use_count() << std::endl;
}

//output
//foo's count is 1
//bar's count is 1
//bar destructed
//foo destructed

可以看到 std::weak_ptr 并没有给引用计数加一,实际上它增加的是控制块的弱引用计数。当 std::weak_ptr 的析构器被调用的时候,它会将弱引用计数减一,如果弱引用计数等于零,那么会删除控制块。其实也不一定需要 foo 和 bar 类中都是 std::shared,只要其中之一是 std::weak_ptr 即可。

原文链接: https://www.cnblogs.com/riasartemis/p/17099954.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    C++中的共享指针以及循环引用

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/316459

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月16日 下午2:15
下一篇 2023年2月16日 下午2:16

相关推荐