【weak_ptr不为人知的小妙用】

【weak_ptr不为人知的小妙用】

  • 一、weak_ptr常见用法
    • 1、weak_ptr的原理
    • 2、使用方法
  • 二、崩溃问题背景
    • 1、不使用智能指针
    • 2、使用shared_ptr管理
  • 三、weak_ptr生命周期控制
  • 四、总结&延伸

一、weak_ptr常见用法

1、weak_ptr的原理

  weak_ptr是为了解决shared_ptr循环引用而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,即将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。不论是否有weak_ptr指向,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。

2、使用方法

  当weak_ptr指向的对象被释放后,需要用其lock函数或者expired()函数来判断。
  lock():对象存在,则返回一个指向共享对象的shared_ptr(引用计数会增1)
对象不存在,则返回nullptr
  expired():所指的内存是否被释放

二、崩溃问题背景

1、不使用智能指针

#include 
#include  // shared_ptr
class A {
public:
    void render() {}
};

class B {
public:
    B(A* pa) : m_pa(pa) {
    }
    void doRender() { 
        m_pa->render();
    }
private:
    A* m_pa;
};

int main()
{
    A* pa = new A();
    B* pb = new B(pa);

    delete pa;
    pa = nullptr;
    
    pb->doRender();

    delete pb;
    pb = nullptr;
    return 0;
}

  如上所示:给B传入一个A对象的指针并保存,当A对象删除后B对象仍然访问A对象指针时就会导致野指针崩溃。这是一个老生常谈的问题了,解决的办法是创建shared_ptr来管理 A 的实例,然后传给B,就可以避免手动管理了:

2、使用shared_ptr管理

class B {
public:
    B(std::shared_ptr<A> pa) : m_pa(pa) {}
    void doRender() { 
        m_pa->render();
    }
private:
    std::shared_ptr<A> m_pa; 
};

int main()
{
	// A对象创建之初就要用智能指针管理起来,不能中间将裸指针转成智能指针哦
    std::shared_ptr<A> pa = std::make_shared<A>();
    B* pb = new B(pa);
    
    pb->doRender();

    delete pb;
    pb = nullptr;
    return 0;
}

  若对A的生命周期没有特殊要求,到这就结束了,坐等程序结束时自动回收A的内存。
但实际场景中经常会有些特殊情况:比如当A被销毁时B还要继续存在,且不能影响B的功能,此时就不能使用shared_ptr去管理A了(B一直持有A的引用计数,其内存一直不会被释放)。
  此时不得不使用A的裸指针,这种情况下应该如何解决开篇讲到的崩溃问题呢?接下来就要讲到一直被用来解决循环引用问题的weak_ptr了。

三、weak_ptr生命周期控制

#include 
#include  // shared_ptr
class A {
public:
    A() { m_life_cycle = std::make_shared<bool>(true); }
    std::shared_ptr<bool> getLifeCycle() const {
         return m_life_cycle; 
    }
    void render() {}

    std::shared_ptr<bool> getLifeCycle() {
        return m_life_cycle; 
    }
private:
	// A中新增生命周期控制变量m_life_cycle
    std::shared_ptr<bool> m_life_cycle;
};

class B {
public:
    B(A* pa) : m_pa(pa) {
        if (pa) {
        	// weak_ptr指针初始化只能靠shared_ptr指针赋值
            m_life_cycle = pa->getLifeCycle();
        }
    }
    void doRender() { 
        if (!m_life_cycle.expired()) {
            m_pa->render(); 
        }
    }
private:
    A* m_pa;
    std::weak_ptr<bool> m_life_cycle;
};

int main()
{
    A* pa = new A();
    B* pb = new B(pa);

    delete pa;
    pa = nullptr;
    
    pb->doRender();

    delete pb;
    pb = nullptr;
    return 0;
}

  如上所示:要主动控制A的生命周期,但是又不能影响B的生命周期,那么B中还是只能持有A的裸指针,然后在A中新加一个shared_ptr m_life_cycle去监控其自身的内存状态,创建B对象时传入A的指针并在B中搞一个weak_ptr m_life_cycle去接收A的m_life_cycle。简单类图:
【weak_ptr不为人知的小妙用】_第1张图片

  如此一来,当A对象被删除时B对象在调用A的函数之前判断这个weak_ptr指向的内存是否还在,如果已经释放就不会再去访问A的内存了,达到避免野指针的问题。

四、总结&延伸

  除了此应用场景,在观察者模式中weak_ptr也能起到多线程访问控制的作用,具体可参考:https://blog.csdn.net/qq_38410730/article/details/105903979
  对于weak_ptr,感觉大部分人还停留在其只能解决shared_ptr循环引用问题的刻板印象上,但是对于其控制对象生命周期的本质功能,达不到灵活运用的境界。什么时候该用shared_ptr,什么时候该用weak_ptr,不能精准的使用,借修改bug的机会,写以此文记录,愿与诸君共同进步,真正精通C艹。
                    “精准与否,就是屠宰和手术的区别” ----《青钢影·卡蜜尔》

你可能感兴趣的:(c++,算法,设计模式)