第22課 weak_ptr弱引用智能指針

一. weak_ptr的概況ios

(一)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():獲取當前控制塊中資源的強引用計數。less

  2. expired():判斷所觀測的資源是否失效(即己經被釋放),即use_count是否爲0。ide

    (1)shared_ptr<int> sp1 = wp.lock();//若是wp失效,則sp爲空(其中wp爲weak_ptr類型)函數

    (2)shared_ptr<int> sp2(wp); //若是wp失效,則拋std::bad_weak_ptr異常this

  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
*/
相關文章
相關標籤/搜索