C++智能指針


C++智能指針

Figure1

如果你是一名C++開發者或者參與過C++開發,那麼指針對於你來說一定不陌生吧。指針指向一塊內存空間,當這塊內存空間不再被使用的時候,就需要將其釋放。Java,Go等語言自帶垃圾回收機制,對於這類不再被使用的內存空間,他們會自動進行釋放。C++則不是,C++本身並沒有垃圾回收機制,當一塊內存不再被使用的時候,需要代碼來進行釋放。而這就常常會導致一個問題,那就是忘記釋放內存引發內存洩漏。

C++智能指針

Figure2

我們來看Figure2中的這段代碼,很明顯的一個內存洩漏的demo。這段代碼在堆上開闢了一塊內存空間,並由指針f1指向之。代碼在執行完成之後,沒有將內存釋放。正確的做法就是delete這個指針,如圖Figure3。

C++智能指針

Figure3

然而有很多程序員常常忘記釋放內存這一步,一段代碼中大量的delete也並不美觀,甚至有可能因為代碼拋出異常導致delete沒能被執行。此時有一種更好的解決方案,便是智能指針。

C++標準庫中提供的智能指針有四種:shared_ptr, unique_ptr, weak_ptr以及auto_ptr。其中auto_ptr已於C++11被棄用。本文中筆者主要談談另外三種智能指針。它們都在頭文件memory裡面,使用時需要先#include<memory>

C++ Primer這本書中對智能指針的描述是這樣的:A smart pointer acts like a regular pointer with the important exception that it automatically deletes the object to which it points. 比起普通指針,智能指針最最關鍵的區別便是它能夠自動釋放分配的內存空間。智能指針會知道這塊內存是否還在被使用,如果沒有,則釋放之。

接下來說說目前常用的三種智能指針是如何工作的。


(1) shared_ptr

名字叫shared,就是說,一塊內存空間可以被多個這種指針所共享。如圖Figure4,兩個shared_ptr, p1和p2,都指向了同一塊內存空間。

C++智能指針

Figure4

那麼shared_ptr如何得知啥時候該釋放內存呢?它維護了一個引用計數(reference count)。何謂引用計數呢?就是,當前該shared_ptr所指向的內存空間,一共被多少個其它的shared_ptr指向。比如圖Figure4中,指針p1和指針p2的引用計數都是2,因為當前空間被兩個指針指向。假設此時有個新的shared_ptr指向了這塊內存空間,那麼引用計數就會變成3。當引用計數變成0的時候,也就是沒有別的shared_ptr指向這塊內存空間的時候

,這塊內存就會被釋放。只要shared_ptr離開了作用域,內存就會被釋放,無需代碼中delete。通常有兩種創建這種指針的方法,如圖Figure5。

C++智能指針

Figure5

(2) unique_ptr

unique,唯一的。也就是說,一塊內存空間只會由一個unique_ptr獨佔,不會有兩個unique_ptr指向同一個內存空間。與此同時,unique_ptr也正是在C++11中被廢棄的auto_ptr的替代品,兩者均是用於獨佔一個內存空間。一個unique_ptr是無法給另一個unique_ptr賦值的,因為它們不能夠指向同一塊內存空間。如圖Figure6便是一個錯誤範例。

C++智能指針

Figure6

這段代碼將會編譯失敗。如果希望將一個unique_ptr指向另一塊內存空間,需要使用reset函數。

C++智能指針

Figure7

Figure7中這個名叫us的unique_ptr指向了新的內存空間f2,而它原本指向的內存空間f在調用reset的時候便會被釋放掉。

(3) weak_ptr

該指針通常和shared_ptr一同使用。上文中提到了shared_ptr維護了一個引用計數,當一個weak_ptr指向了一個由shared_ptr管理的內存空間時,不會改變shared_ptr的引用計數。所以說,weak_ptr共享shared_ptr的內存空間“weakly”。當shared_ptr要釋放所管理的內存空間時,即便有weak_ptr指向之,依舊會釋放內存空間。因此通常不會通過weak_ptr來訪問內存空間,不然很容易發生空指針。weak_ptr的主要作用是預防懸掛指針(dangling pointer)。如果一個指針指向了一塊已經失效了的內存空間,那麼訪問這個指針就會引發異常。而weak_ptr有一個作用就是它能知道它所指向的內存空間是否存活。weak_ptr自帶一個函數expired()能告知內存空間是否依舊有效。如果該函數返回了true則代表內存空間已經失效。


智能指針固然好用,但使用仍需謹慎,主要需要預防懸掛指針的出現。智能指針維護的內存空間儘量不要有其它常規指針指向,不然就容易出現智能指針釋放了內存空間而常規指針依舊指向這塊已經失效的內存空間而引發懸掛指針的問題。


分享到:


相關文章: