您好,登錄后才能下訂單哦!
這篇文章主要講解了“C語言智能指針之怎么使用weak_ptr”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“C語言智能指針之怎么使用weak_ptr”吧!
weak_ptr
這個指針天生一副“小弟”的模樣,也是在C++11的時候引入的標準庫,它的出現完全是為了彌補它老大shared_ptr天生有缺陷的問題,其實相比于上一代的智能指針auto_ptr
來說,新進老大shared_ptr
可以說近乎完美,但是通過引用計數實現的它,雖然解決了指針獨占的問題,但也引來了引用成環的問題,這種問題靠它自己是沒辦法解決的,所以在C++11的時候將shared_ptr和weak_ptr一起引入了標準庫,用來解決循環引用的問題。
weak_ptr
本身也是一個模板類,但是不能直接用它來定義一個智能指針的對象,只能配合shared_ptr
來使用,可以將shared_ptr
的對象賦值給weak_ptr,并且這樣并不會改變引用計數的值。查看weak_ptr的代碼時發現,它主要有lock
、swap
、reset
、expired
、operator=
、use_count
幾個函數,與shared_ptr
相比多了lock、expired函數,但是卻少了get函數,甚至連operator* 和 operator->都沒有,可用的函數數量少的可憐,下面通過一些例子來了解一下weak_ptr的具體用法。
1.VS2015 + Windows7(應該是C++11標準)
2.頭文件#include <memory>
3.命名空間using namespace std;
1.weak_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_b->set_ptr(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的對象并沒有被析構,其中的引用效果如下圖所示,起初定義完ptr_a
和ptr_b
時,只有①③兩條引用,然后調用函數set_ptr后又增加了②④兩條引用,當test_refer_to_each_other
這個函數返回時,對象ptr_a和ptr_b被銷毀,也就是①③兩條引用會被斷開,但是②④兩條引用依然存在,每一個的引用計數都不為0,結果就導致其指向的內部對象無法析構,造成內存泄漏。
解決這種狀況的辦法就是將兩個類中的一個成員變量改為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
的對象都被正常的析構了,引用關系如下圖所示,流程與上一例子相似,但是不同的是④這條引用是通過weak_ptr
建立的,并不會增加引用計數,也就是說CA的對象只有一個引用計數,而CB的對象只有2個引用計數,當test_refer_to_each_other
這個函數返回時,對象ptr_a
和ptr_b
被銷毀,也就是①③兩條引用會被斷開,此時CA對象的引用計數會減為0,對象被銷毀,其內部的m_ptr_b成員變量也會被析構,導致CB對象的引用計數會減為0,對象被銷毀,進而解決了引用成環的問題。
2. 測試weak_ptr
對引用計數的影響
其實weak_ptr本身設計的很簡單,就是為了輔助shared_ptr
的,它本身不能直接定義指向原始指針的對象,只能指向shared_ptr
對象,同時也不能將weak_ptr
對象直接賦值給shared_ptr類型的變量,最重要的一點是賦值給它不會增加引用計數:
void test1() { // 編譯錯誤 // error C2665: “std::weak_ptr<CA>::weak_ptr”: 3 個重載中沒有一個可以轉換所有參數類型 // weak_ptr<CA> ptr_1(new CA()); shared_ptr<CA> ptr_1(new CA()); cout << "ptr_1 use count : " << ptr_1.use_count() << endl; // 輸出:ptr_1 use count : 1 shared_ptr<CA> ptr_2 = ptr_1; cout << "ptr_1 use count : " << ptr_1.use_count() << endl; // 輸出:ptr_1 use count : 2 cout << "ptr_2 use count : " << ptr_2.use_count() << endl; // 輸出:ptr_1 use count : 2 weak_ptr<CA> wk_ptr = ptr_1; cout << "ptr_1 use count : " << ptr_1.use_count() << endl; // 輸出:ptr_1 use count : 2 cout << "ptr_2 use count : " << ptr_2.use_count() << endl; // 輸出:ptr_1 use count : 2 // 編譯錯誤 // error C2440 : “初始化”: 無法從“std::weak_ptr<CA>”轉換為“std::shared_ptr<CA>” // shared_ptr<CA> ptr_3 = wk_ptr; }
測試weak_ptr常用函數的用法
weak_ptr中只有函數lock和expired兩個函數比較重要,因為它本身不會增加引用計數,所以它指向的對象可能在它用的時候已經被釋放了,所以在用之前需要使用expired函數來檢測是否過期,然后使用lock函數來獲取其對應的shared_ptr對象,然后進行后續操作:
void test2() { shared_ptr<CA> ptr_a(new CA()); // 輸出:CA() called! shared_ptr<CB> ptr_b(new CB()); // 輸出:CB() called! cout << "ptr_a use count : " << ptr_a.use_count() << endl; // 輸出:ptr_a use count : 1 cout << "ptr_b use count : " << ptr_b.use_count() << endl; // 輸出:ptr_b use count : 1 weak_ptr<CA> wk_ptr_a = ptr_a; weak_ptr<CB> wk_ptr_b = ptr_b; if (!wk_ptr_a.expired()) { wk_ptr_a.lock()->show(); // 輸出:this is class CA! } if (!wk_ptr_b.expired()) { wk_ptr_b.lock()->show(); // 輸出:this is class CB! } // 編譯錯誤 // 編譯必須作用于相同的指針類型之間 // wk_ptr_a.swap(wk_ptr_b); // 調用交換函數 wk_ptr_b.reset(); // 將wk_ptr_b的指向清空 if (wk_ptr_b.expired()) { cout << "wk_ptr_b is invalid" << endl; // 輸出:wk_ptr_b is invalid 說明改指針已經無效 } wk_ptr_b = ptr_b; if (!wk_ptr_b.expired()) { wk_ptr_b.lock()->show(); // 輸出:this is class CB! 調用賦值操作后,wk_ptr_b恢復有效 } // 編譯錯誤 // 編譯必須作用于相同的指針類型之間 // wk_ptr_b = wk_ptr_a; // 最后輸出的引用計數還是1,說明之前使用weak_ptr類型賦值,不會影響引用計數 cout << "ptr_a use count : " << ptr_a.use_count() << endl; // 輸出:ptr_a use count : 1 cout << "ptr_b use count : " << ptr_b.use_count() << endl; // 輸出:ptr_b use count : 1 }
引用計數的出現,解決了對象獨占的問題,但是也帶來了循環引用的困擾,使用weak_ptr可以打破這種循環,當你理不清引用關系的時候,不妨采用文中畫圖的方式來理一理頭緒,或許就會有眼前一亮的感覺。
總結 weak_ptr雖然是一個模板類,但是不能用來直接定義指向原始指針的對象。weak_ptr接受shared_ptr類型的變量賦值,但是反過來是行不通的,需要使用lock函數。weak_ptr設計之初就是為了服務于shared_ptr的,所以不增加引用計數就是它的核心功能。由于不知道什么之后weak_ptr所指向的對象就會被析構掉,所以使用之前請先使用expired函數檢測一下。 測試源碼
感謝各位的閱讀,以上就是“C語言智能指針之怎么使用weak_ptr”的內容了,經過本文的學習后,相信大家對C語言智能指針之怎么使用weak_ptr這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。