必須要注意的 C++ 動態記憶體資源管理(三)——智慧指標
阿新 • • 發佈:2019-02-12
七.前言
在前面一節,我們簡單實現了三種類型資源的”指標物件”。其實在c++11的標準庫中已經為我們準備了這樣的指標物件——智慧指標,分別是:shared_ptr , unique_ptr(取代了auto_ptr) , weak_ptr。下面我們簡單來介紹一下這三類智慧指標的特點和適用情況。 |
八.shared_ptr智慧指標
shared_ptr指標適用的就是前一節所說的第3類資源指標。 shared_ptr是模板類,所以可以通過任意的指標型別(這裡介紹使用的是動態分配的指標,但可以不是動態分配建立的,後面會介紹)來初始化。 shared_ptr物件可以進行拷貝,賦值;當一個物件A拷貝B或者B賦值給A,其根本都是將B的資源共享給A,然後將該資源的引用次數增加;當引用該資源的最後一個shared_ptr物件銷燬的時候,會自動把該資源釋放掉。下面看shared_ptr的相關操作: |
函式 | 介紹 |
---|---|
shared_ptr<T>sp(T* res) | 將資源res交給sp來管理,資源不使用時會自動釋放。 |
shared_ptr<T>sp = make_spared(params) | 用params引數構造T型別的物件並且交給sp來管理。 |
p | p可以作為條件判斷,若p指向一個物件,則為true,否則為false。 |
*p | 對shared_ptr<T>物件解引用,得到的是由p管理的T型別物件的引用。 |
p->mem | 等價於(*p).mem |
p.get() | 返回p中儲存的T*指標 |
swap(p,q) | 交換p,q各自內部的T*指標,注意p,q型別要相同 |
p.swap(q) | 同上 |
p = q | p,q必須都是shared_ptr指標,並且各自管理的指標型別能相互轉換。此操作會遞減p的引用次數,遞增q的引用次數;若p的引用次數變為0,則其管理的原記憶體會自動釋放。 |
shared_ptr<T>sp(q) | 使得sp引用q所佔資源。 |
p.reset() , p.reset(q) | 放棄p對資源的引用,若為唯一引用,還會釋放記憶體。若傳有引數q,則讓p引用q所佔資源。 |
p.use_count() | 返回與p共享的智慧指標數量;可能很慢,主要用於除錯。 |
p.unique() | 若p.use_count()為1,返回true,否則返回false |
我們用下面這段簡單的程式碼來理解shared_ptr智慧指標的使用: |
int main()
{
{
//建立一個動態string初始化為"hello wold"並交付給p來管理。
shared_ptr<string> p(new string("hello world"));
{
shared_ptr<string> q(p);//用p初始化q,則該字串增添一個使用者
cout << p.use_count() << endl; // >> 2
cout << *q << endl; // >> "hello world"
*q = *q + "!"; //解引用修改q管理的string物件。
}// 跳出該程式碼塊,q生命週期結束,則該字串使用者又只剩p一個了。
cout << p.use_count() << endl; // >> 1
cout << *p << endl; >> "hello world!"
} // 跳出該程式碼塊,p生命週期結束,則該字串記憶體被釋放。
return 0;
}
事實上,在不是那種臨界資源的情況下,我們管理記憶體資源大多是使用shared_ptr智慧指標。而引用計數實現的有使用者保留,無使用者才釋放記憶體似乎完美地解決了記憶體釋放問題。然而shared_ptr也有缺陷,不過我們留到後面來講。 |
九.unique_ptr智慧指標
unique_ptr指標適用的就是前一節所說的第2類資源指標。 unique_ptr是模板類,所以可以通過任意的指標型別(這裡介紹使用的是動態分配的指標,但可以不是動態分配建立的,後面會介紹)來初始化。 unique_ptr物件不可以進行拷貝,賦值;只能通過相關函式讓unique_ptr讓出的支配權然後其他unique_ptr接收。這樣就保證了支配權的唯一性。下面看unique_ptr的相關操作: |
函式 | 介紹 |
---|---|
unique_ptr<T>up(T* res) | 將資源res交給up來管理,資源不使用時會自動釋放。 |
p | p可以作為條件判斷,若p指向一個物件,則為true,否則為false。 |
*p | 對shared_ptr<T>物件解引用,得到的是由p管理的T型別物件的引用。 |
p->mem | 等價於(*p).mem。 |
p.get() | 返回p中儲存的T*指標。 |
swap(p,q) | 交換p,q各自內部的T*指標,注意p,q型別要相同。 |
p.swap(q) | 同上。 |
p = q | 這是無效操作,unique_ptr不能進行賦值。 |
unique_ptr<T>sp(q) | 這是無效操作,unique_ptr不能進行拷貝。 |
p = nullptr | 釋放p指向的物件,並將p置為控。 |
p.release() | p放棄對指標的控制權,返回資源指標,並將p置為空。 |
p.reset(T* q) | 釋放p指向的物件,並將p置為空,若傳入引數q,則讓p指向q該指標。 |
p.reset(q.release()) | 釋放掉p指向的物件,q將自身指向物件的控制權轉移給p,然後q自身置為空。 |
我們用下面這段簡單的程式碼來理解unique_ptr智慧指標的使用: |
int main()
{
{
//建立一個unique_ptr智慧指標q但是目前沒有管理任何記憶體。
unique_ptr<string> q;
{
//建立一個動態string初始化為"hello wold"並交付給p來管理。
unique_ptr<string> p(new string("hello world"));
cout << *p << endl; // >> "hello world"
q.reset(p.release()); //p將支配權
}// 跳出該程式碼塊,p生命週期結束,則該字串使用者又只剩p一個了。
cout << *q << endl; //>> "hello world"
} // 跳出該程式碼塊,q生命週期結束,則該字串記憶體被釋放。
return 0;
}
特別的,unique_ptr智慧指標還可以用於管理動態陣列: |
int main()
{
//當up釋放資源的時候會自動呼叫 delete[] 銷燬其指標。
unique_ptr<int[]> up(new int[10]);
//指向陣列的unique_ptr指標過載了[]
for(int i = 0 ; i < 10 ; i ++) up[i] = i;
for(int i = 0 ; i < 10 ; i ++)
printf("%d",up[i]); // >> 012345679
return 0;
}
一般來說,我們不常用unique_ptr智慧指標來管理動態記憶體。而是管理類似於資料庫連線物件等資源。 有時我們的資源記憶體不一定是通過動態分配,而且釋放有時候也不僅僅是delete 指標而已。所以,我們以後在使用unique_ptr指標的時候還需要傳入刪除器。這個,我們後面會詳細講到。 |
十.weak_ptr智慧指標
weak_ptr 是一種不控制所指向物件生命期的智慧指標,它指向一個由 shared_ptr 管理的物件。將一個 weak_ptr 繫結到一個 shared_ptr 不會改變 shared_ptr 的引用次數。一旦最後一個指向物件的shared_ptr 被銷燬,物件就會被釋放;即使這個時候存在 weak_ptr 指向了該物件,物件還是會被釋放。所以我們可以把 weak_ptr 看作是一種”弱引用”。 |
函式 | 介紹 |
---|---|
weak_ptr<T>wp | 空 weak_ptr 可以指向型別為T的物件 |
weak_ptr<T>wp(sp) | 與shared_ptr sp指向相同的物件,T必須可以轉化為sp指向物件的型別。 |
w = p | p 可以是weak_ptr或者shared_ptr。賦值後w,p共享物件。但是w不干涉物件宣告週期。 |
w.reset() | m置為空。 |
w.use_count() | 與m共享資源的shared_ptr個數。 |
w.expired() | 若w.use_count()為0,返回true,否則返回false。 |
w.lock() | 如果expired為true,返回一個空的shared_ptr;否則返回指向w物件的shared_ptr。 |
正是因為weak_ptr是一種”弱引用”,所以,很有可能在使用過程中某個weak_ptr被支配的shared_ptr給銷燬了;所以我們在使用weak_ptr之前需要呼叫w.lock()來判斷weak_ptr所指向的記憶體是否被釋放了。 |
基本上,智慧指標的基本操作就講到這裡,下一節會介紹智慧指標的刪除器以及相關注意事項。 |