和 shared_ptr、unique_ptr 類型指針一樣,weak_ptr 智能指針也是以模板類的方式實現的。weak_ptr<T>( T 為指針所指數據的類型)定義在<memory>頭文件,並位於 std 命名空間中。因此,要想使用 weak_ptr 類型指針,程序中應首先包含<memory>頭文件。
需要注意的是,C++11標准雖然將 weak_ptr 定位為智能指針的一種,但該類型指針通常不單獨使用(沒有實際用處),只能和 shared_ptr 類型指針搭配使用。甚至於,我們可以將 weak_ptr 類型指針視為 shared_ptr 指針的一種輔助工具,借助 weak_ptr 類型指針, 可以獲取 shared_ptr 指針的一些狀態信息,比如有多少指向相同的 shared_ptr 指針、shared_ptr 指針指向的堆內存是否已經被釋放等等。
此外,當 weak_ptr 類型指針的指向和某一 shared_ptr 指針相同時,weak_ptr 指針並不會使所指堆內存的引用計數加 1;同樣,當 weak_ptr 指針被釋放時,之前所指堆內存的引用計數也不會因此而減 1。也就是說,weak_ptr 類型指針並不會影響所指堆內存空間的引用計數。
同時,weak_ptr<T> 模板類中沒有重載 * 和 -> 運算符,這也就意味着,weak_ptr 類型指針只能訪問所指的堆內存,而無法修改它。
一、weak_ptr的作用
weak_ptr主要針對shared_ptr的空懸指針和循環引用問題而提出:
(1)空懸指針問題:有兩個指針p1和p2,指向堆上的同一個對象Object,p1和p2位於不同的線程中。假設線程A通過p1指針將對象銷毀了(盡管把p1置為了NULL),那p2就成了空懸指針。
weak_ptr不控制對象的生命期,但是它知道對象是否還活着。如果對象還活着,那么它可以提升為有效的shared_ptr(提升操作通過lock()函數獲取所管理對象的強引用指針);如果對象已經死了,提升會失敗,返回一個空的shared_ptr。
(2)循環引用問題:
#include <iostream> #include <memory> using namespace std; class Parent; class Child; typedef shared_ptr<Parent> parent_ptr; typedef shared_ptr<Child> child_ptr; class Parent { public: ~Parent() { cout << "~Parent()" << endl; } public: child_ptr children; }; class Child { public: ~Child() { cout << "~Child()" << endl; } public: parent_ptr parent; }; int main() { parent_ptr father(new Parent); child_ptr son(new Child); // 父子互相引用 father->children = son; son->parent = father; cout << father.use_count() << endl; // 引用計數為2 cout << son.use_count() << endl; // 引用計數為2 return 0; }
如上代碼,將在程序退出前,father的引用計數為2,son的計數也為2,退出時,shared_ptr所作操作就是簡單的將計數減1,如果為0則釋放,顯然,這個情況下,引用計數不為0,於是造成father和son所指向的內存得不到釋放,導致內存泄露。
使用weak_ptr可以打破這樣的循環引用。由於弱引用不更改引用計數,類似普通指針,只要把循環引用的一方使用弱引用,即可解除循環引用。以上述代碼為例,只要把Child類的代碼修改為如下即可:
class Child { public: ~Child() { cout << "~Child()" << endl; } public: weak_ptr<Parent> parent; };
最后值得一提的是,雖然通過弱引用指針可以有效的解除循環引用,但這種方式必須在能預見會出現循環引用的情況下才能使用,即這個僅僅是一種編譯期的解決方案,如果程序在運行過程中出現了循環引用,還是會造成內存泄漏的。因此,不要認為只要使用了智能指針便能杜絕內存泄漏。
二、weak_ptr的創建
創建一個 weak_ptr 指針,有以下 3 種方式:
(1)創建一個空 weak_ptr 指針:std::weak_ptr<int> wp1;
(2)用已有的 weak_ptr 指針創建一個新的 weak_ptr 指針:weak_ptr<int> wp2 (wp1);
若 wp1 為空指針,則 wp2 也為空指針;反之,如果 wp1 指向某一 shared_ptr 指針擁有的堆內存,則 wp2 也指向該塊存儲空間(可以訪問,但無所有權)。
(3)weak_ptr 指針更常用於指向某一 shared_ptr 指針擁有的堆內存,因為在構建 weak_ptr 指針對象時,可以利用已有的 shared_ptr 指針為其初始化。例如:
1)shared_ptr<int> sp (new int);
2)weak_ptr<int> wp3 (sp);
由此,wp3 指針和 sp 指針有相同的指針。再次強調,weak_ptr 類型指針不會導致堆內存空間的引用計數增加或減少。
三、weak_ptr模板類提供的成員方法
和 shared_ptr<T>、unique_ptr<T> 相比,weak_ptr<T> 模板類提供的成員方法不多,以下是常用的成員方法及各自的功能:
1)operator=():重載 = 賦值運算符,是的 weak_ptr 指針可以直接被 weak_ptr 或者 shared_ptr 類型指針賦值。
2)swap(x):其中 x 表示一個同類型的 weak_ptr 類型指針,該函數可以互換 2 個同類型 weak_ptr 指針的內容。
3)reset():將當前 weak_ptr 指針置為空指針。
4)use_count():查看指向和當前 weak_ptr 指針相同的 shared_ptr 指針的數量。
5)expired():判斷當前 weak_ptr 指針為否過期(指針為空,或者指向的堆內存已經被釋放)。
6)lock():如果當前 weak_ptr 已經過期,則該函數會返回一個空的 shared_ptr 指針;反之,該函數返回一個和當前 weak_ptr 指向相同的 shared_ptr 指針。