C++智能指针之weak_ptr

https://blog.csdn.net/albertsh/article/details/82286999

前言

weak_ptr这个指针天生一副“小弟”的模样,也是在C++11的时候引入的标准库,它的出现完全是为了弥补它老大shared_ptr天生有缺陷的问题,其实相比于上一代的智能指针auto_ptr来说,新进老大shared_ptr可以说近乎完美,但是通过引用计数实现的它,虽然解决了指针独占的问题,但也引来了引用成环的问题,这种问题靠它自己是没办法解决的,所以在C++11的时候将shared_ptrweak_ptr一起引入了标准库,用来解决循环引用的问题。
  weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段.
  weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或 另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少.

namespace boost {
    template<typename T> class weak_ptr {
    public:
        template <typename Y>
        weak_ptr(const shared_ptr<Y>& r);
        weak_ptr(const weak_ptr& r);
 
        ~weak_ptr();
 
        T* get() const;  //对所管理对象的访问

        bool expired() const //检测管理的对象是否已经释放
        {    
            return (this->_Expired());
        }
        shared_ptr<_Ty> lock() const //获取所管理对象的强引用指针
        {   
            return (shared_ptr<_Elem>(*this, false));
        }
    };
}

weak_ptr介绍

  1. 由构造函数可以看出,weak_ptr只能从shared_ptrweak_ptr构造而来
  2. 进行该对象的内存管理、生存期管理的是那个强引用的boost::share_ptr,boost::weak_ptr只是提供了对管理对象的一个访问手段
  3. weak_ptr 支持拷贝或赋值, 但不会影响对应的 shared_ptr 内部对象的计数.
  4. 成员函数介绍
    ① weak_ptr 没有重载*和->
    ② expired 用于检测所管理的对象是否已经释放, 如果已经释放, 返回 true; 否则返回 false
    ③ lock 用于获取所管理的对象的强引用(shared_ptr)。 如果 expired 为 true, 返回一个空的 shared_ptr; 否则返回一个有效的 shared_ptr, 其内部对象指向与 weak_ptr 相同
    ④ use_count 返回与 shared_ptr 共享的对象的引用计数.
    ⑤ reset 将 weak_ptr 置空
  5. weak_ptr通常使用技法
    weak_ptr中只有函数lockexpired两个函数比较重要,因为它本身不会增加引用计数,所以它指向的对象可能在它用的时候已经被释放了,所以在用之前需要使用expired函数来检测是否过期,然后使用lock函数来获取其对应的shared_ptr对象,然后进行后续操作。

shared_ptr带来循环引用的问题

class CB;
class CA
{
public:
    CA() { cout << "CA() called! " << endl; }
    ~CA() { cout << "~CA() called! " << endl; }
    void set_ptr(shared_ptr<CB>& ptr) { m_ptr_b = ptr; }
    void b_use_count() { cout << "b use count : " << m_ptr_b.use_count() << endl; }
    void show() { cout << "this is class CA!" << endl; }
private:
    shared_ptr<CB> m_ptr_b;
};

class CB
{
public:
    CB() { cout << "CB() called! " << endl; }
    ~CB() { cout << "~CB() called! " << endl; }
    void set_ptr(shared_ptr<CA>& ptr) { m_ptr_a = ptr; }
    void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }
    void show() { cout << "this is class CB!" << endl; }
private:
    shared_ptr<CA> m_ptr_a;
};

void test_refer_to_each_other()
{
    shared_ptr<CA> ptr_a(new CA());
    shared_ptr<CB> ptr_b(new CB());

    cout << "a use count : " << ptr_a.use_count() << endl;
    cout << "b use count : " << ptr_b.use_count() << endl;

    ptr_a->set_ptr(ptr_b);  //ptr_a.m_ptr_b = ptr_b;
    ptr_b->set_ptr(ptr_a);  //ptr_b.m_ptr_a = ptr_a;

    cout << "a use count : " << ptr_a.use_count() << endl;
    cout << "b use count : " << ptr_b.use_count() << endl;
}

测试结果如下:
	CA() called!
	CB() called!
	a use count : 1
	b use count : 1
	a use count : 2
	b use count : 2
	//最后没有调用CA和CB的析构函数,

通过结果可以看到,最后CA和CB的对象并没有被析构,其中的引用效果如下图所示,分析:
1- 起初定义完ptr_a和ptr_b时,只有①②两条引用;
2- 然后调用函数set_ptr后又增加了③④两条引用;
3- 当test_refer_to_each_other这个函数返回时,对象ptr_a和ptr_b被销毁(成员函数ptr被销毁),导致①②两条引用会被断开;因为CA和CB对象中的引用计数都为1,所以就导致shared_ptr<T>指向的内部对象无法析构,造成内存泄漏。
在这里插入图片描述

weak_ptr解决循环引用

将两个类中的一个成员变量改为weak_ptr对象,因为weak_ptr不会增加引用计数,使得引用形不成环,最后就可以正常的释放内部的对象,不会造成内存泄漏,比如将CB中的成员变量改为weak_ptr对象,代码如下:

class CB
{
public:
    CB() { cout << "CB() called! " << endl; }
    ~CB() { cout << "~CB() called! " << endl; }
    void set_ptr(shared_ptr<CA>& ptr) { m_ptr_a = ptr; }
    void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }
    void show() { cout << "this is class CB!" << endl; }
private:
    weak_ptr<CA> m_ptr_a;
};

测试结果如下:
	CA() called!
	CB() called!
	a use count : 1
	b use count : 1
	a use count : 1
	b use count : 2
	~CA() called!
	~CB() called!

通过这次结果可以看到,CA和CB的对象都被正常的析构了,引用关系如下图所示,流程与上一例子相似,
在这里插入图片描述
1 - 起初定义完ptr_a和ptr_b时,只有①②两条引用;
2 - 然后调用函数set_ptr后又增加了③④两条引用,与上面不同的是④这条引用是通过weak_ptr建立的,并不会增加引用计数,也就是说CA的对象只有1个引用计数,而CB的对象只有2个引用计数
3 -
(1) 当test_refer_to_each_other函数返回时,对象ptr_a和ptr_b被销毁,那么①②引用会被断开;
(2) 此时CA对象的引用计数会减为0,CA对象将被销毁;
(3) CA对象销毁将会导致其内部的m_ptr_b成员变量也会被析构,导致CB对象的引用计数减一,此时引用计数减为0,使CB对象也被销毁,进而解决了引用成环的问题。

总结

  1. weak_ptr设计之初就是为了服务于shared_ptr的,所以不增加引用计数就是它的核心功能。
  2. 因为不知道什么时候weak_ptr所指向的对象是否有效,所以使用之前请先用expired函数检测一下
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值