C++ 智能指針

在C++中,動態內存的管理通過new 和 delete來進行。
但卻經常存在以下三個常見問題:

  1. 忘記通過delete 釋放內存,這在編程中經常發生,尤其在某個函數內申請的一塊內存作爲臨時使用,但在函數結束後卻沒釋放。這就會導致“內存泄露”問題。
  2. 使用已經釋放過的內存,當有多個指針指向同一塊內存時,某個指針釋放後,卻仍舊使用其他指針,就會導致這種錯誤。通過釋放內存後把指針置爲空,可以解決這種情況。
  3. 同一個塊內存被釋放多次,同樣是多個指針指向同一塊內存時,不當操作會導致這個情況。

所以在新的標準庫裏面,提供了兩種智能指針shared_ptr類 和 unique_ptr類,均在 <memroy> 頭文件

shared_ptr 類

允許多個指針指向同一個對象或內存,創建時需要指明類型
shared_ptr<int> p ;//一個空的shared_ptr,可以指向int

shared_ptr的操作 含義
p 可以當做判斷是否爲空,若p指向一個對象或內存,返回true。否則返回false
*p 獲得p指向的對象的值
p.get() 返回p中保存的指針(不要過多使用,當智能指針釋放後,返回的這個指針指向的內存也會被釋放)
p.swap(q) 交換p,q的指針
shared_ptr<T> p(q) p 是 q(shared_ptr類型)的拷貝,二者會指向同一塊內存
p.unique() 若指向該內存的shared_ptr指針只有一個則返回true,否則返回false,即判斷p.use_count()的值是否爲1
p.use_count() 返回與p共享對象的智能指針數量(注意只有智能指針),如是p.get()返回一個普通指針被拷貝,use_count不會增加,注意該函數會很慢
p.reset() 如果p是唯一的指針,那麼該函數會釋放此對象,並置空。
p.reset(q) q是一個普通指針,那麼會釋放原對象,把p指向q
p.reset(q,d) d是一個刪除器,可以是一個lambda表達式同上,釋放對象時是調用d而不是delete

特點:
每個shared_ptr都關聯一個計數器,通常稱爲引用計數。每當拷貝一個shared_ptr計數器都會增加。每當銷燬一個共享的shared_ptr,計數器就會減小。當計數器減爲0,他就會自動釋放自己所指向的對象或內存。
釋放內存的工作是交給析構函數來完成的。

shared_ptr和new的結合

shared_ptr可以通過new來分配內存,但不能將一個內置指針轉化爲一個只能指針,必須使用直接初始化的形式。如下

shared_ptr<int> p(new int(3);//這裏p指向一個值爲3的int
shared_ptr<int> p = new int(3);//注意,這裏會報錯。必須直接初始化

報錯的原因是,new 返回的是一個int*。

make_shared 函數

最安全的分配和使用動態內存的方法,就是這個make_shared 標準庫函數
該函數在動態內存中分配一個對象並初始化它,返回該對象的shared_ptr。該函數也在
<memory>中。使用make_shared也要求指定創建的對象類型,與模板類相似.

shared_ptr<int> p1 = make_shared<int>(3);//p1指向一個值爲3的int
shared_ptr<string> p2 = make_shared<string>(3,'9');//p2指向一個"999"的string
shared_ptr<int> p3 = make_shared<int>();//p3指向一個值爲0的初始化的int.
shared_ptr的拷貝和賦值
shared_ptr<int> p = make_shared<int>(3);
shared_ptr<int> q(p);//q也指向了這塊內存,計數器會加一
shared_ptr<int> r = p;//r也指向了這塊內存,計數器再加一

切記不要混用普通指針和智能指針。
如果使用普通指針通過.get()和智能指針指向同一個塊地址,智能指針的計數器是不會增加的。當智能指針釋放內存後。普通指針就會變成懸空指針(指向的內存被釋放)如果再被使用,就會不安全且不可靠。

unique_ptr 類

unique_ptr會“獨享”它所指向的對象,任何時刻都時只能有一個unique_ptr指向對象。unique_ptr被銷燬時,它所指向的對象也會被銷燬。
與shared_ptr不同,沒有對應的類似make_shared的標準函數返回一個unqiue_ptr,所以必須使用new 返回的指針來進行初始化。即unique_ptr必須使用直接初始化形式。

unique_ptr<int> p1;//可以指向一個int的unique_ptr
unique_ptr<int> p2(new int(3));//指向一個值爲3的int
unique_ptr<int> p3(p2);//報錯,無法拷貝
unique_ptr<int> p3 = p2;//報錯,unique_ptr不支持賦值,包括其他類型的地址也無法直接賦值
操作 含義
unique_ptr<T> u 空的unique_ptr,可以指向T類型的對象
u.release() 放棄對指針的控制,返回指針,並把u置空
u.reset() 釋放u所指向的對象
u.reset(q) 如果q時一個指針,那麼u指向這個對象

通過releae或reset可以實現指針的轉移。

unique_ptr<int> u1(new int(3));
unique_ptr<int> u2(u1.release());//u2指向這個值爲3的int。u1會置空.
unique_ptr<int> u3(new int(2));
u3.reset(u2);//u3會釋放原內存,現在指向值爲3的int

參看書籍:

  • 《C++ primer》 第五版
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章