1.auto_ptr,share_ptr概述
在開發過程中,曾經使用過兩種C++的智能指針:auto_ptr和shared_ptr如今,便總結一下,順便比較比較二者使用中的區別,注意避免入坑的危險。
我們知道,在C++中,如果創建一個指向某個對象的指針,那么在使用完這個對象之后我們需要自己delete它。否則,會造成一個懸垂指針(dangling pointer),非常容易造成內存泄漏。事實上,如果在使用指針的同時,程序運行拋出異常,那么所指向的對象仍然不會被安全刪除,依然可能會出現內存泄露的危險。與普通的指針相比而言,智能指針的區別在于它實際上是對普通指針加了一層封裝機制,這樣的一層封裝機制的目的是使智能指針可以方便的管理一個對象的生命期。在生命期結束的時候,自動的刪除內存中被占用的區域。也就是說,智能指針的出現實際上就是為了可以方便的控制對象的生命期,在智能指針中,一個對象什么時候和在什么條件下要被析構或者是刪除是受智能指針本身決定的,用戶并不需要管理,以此來增強程序的健壯性。
A.auto_ptr
它是C++標準庫提供的一種智能指針,在構造時獲取某個對象的所有權,在析構時,釋放該對象RAII(Resource Acquisition Is Initialization),也稱為“資源獲取就是初始化”,是C++語言的一種管理資源、避免泄漏的慣用法。C++標準保證任何情況下,已構造的對象最終會銷毀,即它的析構函數最終會被調用。
簡單的說,RAII的做法是使用一個對象,在其構造時獲取資源,在對象生命期控制對資源的訪問使之始終保持有效,最后在對象析構的時候釋放資源。
如下例所示,即為auto_ptr的使用規則:
int *p = new int(10);
std::auto_ptr <int> ap(p);
但是使用auto_ptr,有一些必須注意的問題:
- (1)不可以將兩個或兩個以上的auto_ptr指向同一個對象,因為其中當其中一個auto_ptr的生命期結束時,它就會析構對象,而另一個auto_ptr在析構時,將造成多重析構問題,這是很不安全的內存操作。故而,以下的行為是一定要避免的:
int *p = new int(0);
auto_ptr<int> ap1(p);
auto_ptr<int> ap2(p);
//ap1和ap2都指向一個對象,這就是極危險的,必須防止這么使用
- (2)不可使用auto_ptr來管理數組指針
因為auto_ptr在析構時,使用的是delete,而不是delete[]。所以以下的使用方式是絕對要避免的。
int *pa = new int[10];
auto_ptr<int> ap(pa);
//這種寫法是完全錯誤的,極可能引起內存泄露的危險
- (3)
auto_ptr強調對“裸”指針的完全占有性。也就是說,一個“裸”指針不能同時被兩個以上的“裸”指針所擁有。那么,在拷貝構造函數和賦值中,auto_ptr均采取了“所有權轉移”的策略,即原指針將失去對裸指針的所有權。
int *p = new int (10);
auto_ptr<int> ap1(p);
auto_ptr<int> ap2 = ap1; //這會使得ap1變成了NULL
cout << *ap1 <<endl; //錯誤!此時,對其解引用是不安全的。
如上的錯誤相對而言,比較容易避免,但是對于以下的代碼,錯誤就較難發現。
void fun(auto_ptr<int> ap)
{
cout << *ap << endl;
}
auto_ptr<int> ap(new int(0) ) ;
fun(ap);
cout << *ap << endl;
//錯誤!對空指針的解引用
上述情況比較隱蔽,這是因為在函數調用時,傳參引起了拷貝構造函數的調用,所以原來的指針失去了其所有權。
- (4)auto_ptr不具備值語義,所以auto_ptr不能被用在STL容器中。
所謂值語義:使之符合以下條件的類型,設有類Obj:
Obj a;
Obj b(a);
Obj c;
c = a;
那么a== b, a == c顯然,auto_ptr并不具有這個特點。
auto_ptr用法要點:
A. 需要包含頭文件<memory>
B.Constructor:explicit auto_ptr(X* p = 0) throw(); 將指針p交給auto_ptr對象托管。
C.Copy constructor:auto_ptr(constauto_ptr&) throw(); template<class Y> auto_ptr(constauto_ptr<Y>& a) throw(); 指針的托管權會發生轉移。
D.Destructor: ~auto_ptr(); 釋放指針p指向的空間。
E. 提供了兩個成員函數 Xget() const throw(); //返回保存的指針
F. 對象中仍保留指針 Xrelease() const throw(); //返回保存的指針,對象中不保留指針auto_ptr實現關鍵點:
A.利用特點“棧上對象在離開作用范圍時會自動析構”。
B.對于動態分配的內存,其作用范圍是程序員手動控制的,這給程序員帶來了方便但也不可避免疏忽造成的內存泄漏,畢竟只有編譯器是最可靠的。
C.auto_ptr通過在棧上構建一個對象a,對象a中wrap了動態分配內存的指針p,所有對指針p的操作都轉為對對象a的操作。而在a的析構函數中會自動釋放p的空間,而該析構函數是編譯器自動調用的,無需程序員操心。
B.shared_ptr
鑒于auto_ptr所出現的無法復制,且不能滿足標準容器對元素的要求,所以boost庫中提供了一種新型的智能指針shared_ptr,它通過引用計數(reference counting)的原理,解決了多個指針之間共享對象所有權的問題,可以被自由地拷貝和賦值,在任意的地方共享它,當沒有代碼使用(引用計數為0)它時才刪除被包裝的動態分配的對象。 shared_ptr也可以安全地放到標準容器中,并彌補了auto_ptr因為轉移語義而不能把指針作為STL容器元素的缺陷。
在C++11中,shared_ptr在<memory>中被定義為: template<class T > class shared_ptr;
std::shared_ptr是通過指針保持某個對象的共享擁有權的智能指針。若干個shared_ptr對象可以擁有同一個對象;最后一個指向該對象的shared_ptr被銷毀或重置時,該對象被銷毀。銷毀該對象時使用的是delete表達式或者是在構造shared_ptr時傳入的自定義刪除器(deleter)。
shared_ptr也可以不擁有對象,稱作空(empt))。 shared_ptr滿足CopyConstructible和CopyAssignable的要求。
- shared_ptr實現細節
成員類型 | 定義 |
---|---|
element_type T | The type of the managed object 即被shared_ptr管理著的對象 |
- 包含的成員函數如下所示:
成員函數 | 成員函數 |
---|---|
(constructor) | constructsnew shared_ptr |
(destructor) | 如果沒有更多shared_ptrs的的鏈接解構擁有的對象 |
operator= | 分配shared_ptr |
reset | 取代管理的對象 |
swap | 交換所管理的對象 |
get | 返回一個指針,指向被管理對象 |
operator* /operator-> | 解引用指針到的管理對象 |
use_count | shared_ptr對象指的是在同一個管理對象的數量 |
unique | 檢查是否被管理對象的管理僅由當前shared_ptr的實例 |
operator bool | 檢查是否有相關的管理對象 |
owner_before | Owner-based ordering |
智能指針share_ptr的實現基于引用計數實現,有機會分析一個引用計數的原理。