zoukankan      html  css  js  c++  java
  • 第22课 weak_ptr弱引用智能指针

    一. weak_ptr的概况

    (一)weak_ptr的创建

      1. 直接初始化:weak_ptr<T> wp(sp); //其中sp为shared_ptr类型

      2. 赋值: wp1 = sp; //其中sp为shared_ptr类型

            wp2 = wp1; //其中wp1为weak_ptr类型

    (二)常用操作

      1. use_count():获取当前控制块中资源的强引用计数。

      2. expired():判断所观测的资源是否失效(即己经被释放),即use_count是否为0。

        (1)shared_ptr<int> sp1 = wp.lock();//如果wp失效,则sp为空(其中wp为weak_ptr类型)

        (2)shared_ptr<int> sp2(wp); //如果wp失效,则抛std::bad_weak_ptr异常

      3. lock():获取所监视资源的shared_ptr,如shared_ptr<int> sp = wp.lock(); //wp为weak_ptr类型。

      4. reset():重置weak_ptr,影响弱引用计数

    (三)注意事项

      1. weak_ptr不是独立的智能指针,它是shared_ptr的助手,只是监视shared_ptr管理的资源是否释放,不会影响强引用计数,不能管理资源。

      2.weak_ptr没有重载操作符*和->,因为它不共享指针,不能操作资源。

      3.weak_ptr主要用来代替可能空悬的shared_ptr

    【编程实验】weak_ptr初体验

    #include <iostream>
    #include <memory>
    
    using namespace std;
    
    int main()
    {
        auto sp1 = make_shared<int>(10);
        weak_ptr<int> wp(sp1);  //通过shared_ptr初始化
        weak_ptr<int> wp1, wp2;
        wp1 = sp1;   //利用shared_ptr来赋值
        wp2 = wp;    //利用weak_ptr赋值
        auto sp2 = wp2.lock(); //sp2为shared_ptr类型
    
        sp1 = nullptr;
    
        cout << wp2.use_count() << endl; //1,强引用计数
        return 0;
    }

    二. weak_ptr的应用

    (一)缓存对象

      1. 考虑一个工厂函数loadWidget,该函数基于唯一ID来创建一些指向只读对象的智能指针。

      2. 假设该只读对象需要被频繁使用,而且经常需要从文件或数据库中加载。那么可以考虑将对象缓存起来。同时为了避免过量缓存,当不再使用时,则将该对象删除。

      3. 由于带缓存,工厂函数返回unique_ptr类型显然不合适。因为调用者和缓存管理器均需要一个指向这些对象的指针。

      4. 当用户用完工厂函数返回的对象后,该对象会被析构,此时相应的缓存条目将会空悬。因为可以考虑将工厂函数的返回值设定为shared_ptr类型,而缓存类型为weak_ptr类型

    (二)观察者模式

       1. 观察者模式是在subject状态发生改变时,通知观察者的一种设计模式。

       2. 在多数实现中,每个subject持有指向观察者的指针,这使得当subject状态改变时可以很容易通知观察者。

       3. subject不会控制其观察者的生存期,因此应该是持有观察者的weak_ptr指针。同时在subject的使用某个指针时,可以先确定是否空悬。

    (三)解决循环引用

      

      1. A、B、C三个对象的数据结构中,A和C共享B的所有权,因此各持有一个指向B的std::shared_ptr;

      2. 假设有一个指针从B指回A(即上图中的红色箭),则该指针的类型应为weak_ptr,而不能是裸指针或shared_ptr,原因如下:

       ①假如是裸指针,当A被析构时,由于C仍指向B,所以B会被保留。但B中保存着指向A的空悬指针(野指针),而B却检测不出来,但解引用该指针时会产生未定义行为。

       ②假如是shared_ptr时。由于A和B相互保存着指向对方的shared_ptr,此时会形成循环引用,从而阻止了A和B的析构。

       ③假如是weak_ptr,这可以避免循环引用。假设A被析构,那么B的回指指针会空悬,但B可以检测到这一点,同时由于该指针是weak_ptr,不会影响A的强引用计数,因此当shared_ptr不再指向A时,不会阻止A的析构。

    (四)监视this智能指针:见《第21课》中的enable_shared_from_this,其中的weak_this_指针即为weak_ptr类型,用于监视this指针。

    【编程实验】weak_ptr的使用

    #include <iostream>
    #include <memory> //for smart pointer
    #include <unordered_map> //for unordered_map
    #include <set>
    
    using namespace std;
    
    class Widget
    {
    public:
        Widget(int id):ID(id){}
        
        int ID;
    };
    
    //1. 利用weak_ptr来缓存对象
    //模拟从数据库中加载,并创建shared_ptr指向widget对象
    shared_ptr<Widget> loadWidget(int WidgetID)
    {
        return make_shared<Widget>(WidgetID); 
    }
    
    //带缓存的工厂函数
    std::shared_ptr<const Widget> fastloadWidget(int WidgetID) //返回shared_ptr类型
    {
        //缓存:weak_ptr类型
        static std::unordered_map<int, std::weak_ptr<const Widget>> cache;
    
        auto objPtr = cache[WidgetID].lock(); //objPtr的类型为shared_ptr,指向缓存的对象
    
        if (!objPtr) { //如果对象不在缓存中. 这里省略了缓存中因失效而不断累积std::weak_ptr的处理。
            objPtr = loadWidget(WidgetID);
            cache[WidgetID] = objPtr;
        }
    
        return objPtr;
    }
    
    //2. 观察者模式
    //2.1 观察者
    class WeatherObservers //抽象观察者
    {
    public:
        virtual void updateWeatherInfo(int num) = 0;
    };
    //机场:具体观察者
    class Airport : public WeatherObservers
    {
    public:
        void updateWeatherInfo(int num) override
        {
            std::cout <<"Airport: " << num << endl;
        }
    };
    //学校:具体观察者
    class School : public WeatherObservers
    {
    public:
        void updateWeatherInfo(int num) override
        {
            std::cout << "School: " << num << endl;
        }
    };
    
    //2.1 主题(气象站)
    class WeatherStation
    {
        using ObserverPtr = std::weak_ptr<WeatherObservers>; //弱引用
    
        //set集合中保存观察者的弱引用(以ObserverPtr为关键字,基于ownership排序)
        using ObserverList = std::set<ObserverPtr, std::owner_less<ObserverPtr>>;
    
        ObserverList obs; //保存所有观察者
    public:
        //注册观察者
        void registerObserver(const ObserverPtr oPtr)
        {
            if (obs.find(oPtr) == obs.end()) {
                obs.insert(oPtr);
            }
        }
        //注销观察者
        void unregisterObserver(const ObserverPtr oPtr) //oPtr为weak_ptr类型
        {
            if (obs.find(oPtr) != obs.end())
            {
                obs.erase(oPtr);
            }
        }
    
        //通知各个观察者
        void notifyObservers(int num)
        {
            std::shared_ptr<WeatherObservers> tempPtr;
            for (auto& ob : obs)
            {
                if ((tempPtr = ob.lock())) {
                    tempPtr->updateWeatherInfo(num);
                }
            }
        }
    };
    
    
    int main()
    {
        //观察者模式
        WeatherStation station;
        std::shared_ptr<Airport> airport(new Airport());
        std::shared_ptr<School>  school(new School());
    
        station.registerObserver(airport);
        station.registerObserver(school);
    
        station.notifyObservers(1);
    
        station.unregisterObserver(school);
        station.notifyObservers(2);
    
        return 0;
    }
    /*输出结果
    Airport: 1
    School: 1
    Airport: 2
    */
  • 相关阅读:
    jQuery.getJSON的缓存问题的解决办法
    MFC Tab Control控件的详细使用
    JavaScript 闭包深入理解(closure)
    STL中sort函数用法简介
    STL中qsort的七种用法
    学习Javascript闭包(Closure)
    使用 Visual Studio 分析器找出应用程序瓶颈
    各种语言性能测试工具一览表
    Javascript 链式作用域
    MessageBox、::MessageBox 、AfxMessageBox三者的区别 .
  • 原文地址:https://www.cnblogs.com/5iedu/p/11623757.html
Copyright © 2011-2022 走看看