智能指针
智能指針和普通指針的區別在于智能指針實際上是對普通指針加了一層封裝機制,這樣的一層封裝機制的目的是為了使得智能指針可以方便的管理一個對象的生命期。
智能指針主要是預防不當的析構行為,防止出現懸垂指針。
在C++中,我們知道,如果使用普通指針來創建一個指向某個對象的指針,那么在使用完這個對象之后我們需要自己刪除它,例如:
ObjectType* temp_ptr = new ObjectType();
temp_ptr->foo();
delete temp_ptr;
很多材料上都會指出說如果程序員忘記在調用完temp_ptr之后刪除temp_ptr,那么會造成一個懸掛指針(dangling pointer),也就是說這個指針現在指向的內存區域其內容程序員無法把握和控制,也可能非常容易造成內存泄漏。
可是事實上,不止是“忘記”,在上述的這一段程序中,如果foo()在運行時拋出異常,那么temp_ptr所指向的對象仍然不會被安全刪除。
在這個時候,智能指針的出現實際上就是為了可以方便的控制對象的生命期,在智能指針中,一個對象什么時候和在什么條件下要被析構或者是刪除是受智能指針本身決定的,用戶并不需要管理。
根據具體的條件,我們一般會討論這樣幾種智能指針,而如下所說的這些智能指針也都是在boost library里面定義的
1) scoped_ptr:
這是比較簡單的一種智能指針,正如其名字所述,scoped_ptr所指向的對象在作用域之外會自動得到析構
此外,scoped_ptr是non-copyable的,也就是說你不能去嘗試復制一個scoped_ptr的內容到另外一個scoped_ptr中,這也是為了防止錯誤的多次析構同一個指針所指向的對象。
2) shared_ptr:
很多人理解的智能指針其實是shared_ptr這個范疇。
正如同學的答案所提到的,shared_ptr中所實現的本質是引用計數(reference counting),也就是說shared_ptr是支持復制的,復制一個shared_ptr的本質是對這個智能指針的引用次數加1,而當這個智能指針的引用次數降低到0的時候,該對象自動被析構,。
需要特別指出的是,如果shared_ptr所表征的引用關系中出現一個環,那么環上所述對象的引用次數都肯定不可能減為0那么也就不會被刪除,為了解決這個問題引入了weak_ptr。
3) weak_ptr:
對weak_ptr起的作用,很多人有自己不同的理解,我理解的weak_ptr和shared_ptr的最大區別在于weak_ptr在指向一個對象的時候不會增加其引用計數,因此你可以用weak_ptr去指向一個對象并且在weak_ptr仍然指向這個對象的時候析構它,此時你再訪問weak_ptr的時候,weak_ptr其實返回的會是一個空的shared_ptr。
實際上,通常shared_ptr內部實現的時候維護的就不是一個引用計數,而是兩個引用計數,一個表示strong reference,也就是用shared_ptr進行復制的時候進行的計數,一個是weak reference,也就是用weak_ptr進行復制的時候的計數。weak_ptr本身并不會增加strong reference的值,而strong reference降低到0,對象被自動析構。
為什么要采取weak_ptr來解決剛才所述的環狀引用的問題呢?需要注意的是環狀引用的本質矛盾是不能通過任何程序設計語言的方式來打破的,為了解決環狀引用,第一步首先得打破環,也就是得告訴C++,這個環上哪一個引用是最弱的,是可以被打破的,因此在一個環上只要把原來的某一個shared_ptr改成weak_ptr,實質上這個環就可以被打破了,原有的環狀引用帶來的無法析構的問題也就隨之得到了解決。
4) intrusive_ptr:
簡單的說,intrusive_ptr和shared_ptr的區別在于intrusive_ptr要求其所指向的對象本身實現一個引用計數機制,也就是說當對象本身包含一個reference counter的時候,可以使用intrusive_ptr。
*************************************************************************************************
#include <utility>
#include <iostream>
using namespace std;
class A
{
public:
A() { id = ++count; cout << "create A" << id << "
"; }
~A() { cout << "destroy A" << id << "
"; }
private:
static int count;
int id;
};
int A::count = 0;
/*調用該函數會丟失掉所有權*/
void sink(auto_ptr<A> a)
{
cout << "Enter sink()
";
}
/*調用該函數會創建對象,并獲取所有權*/
auto_ptr<A> create()
{
cout << "Enter create()
";
auto_ptr<A> a(new A());
return a;
}
int main(int argc, char *argv[])
{
auto_ptr<A> a1 = create();
auto_ptr<A> a2 = a1; /*轉移所有權,此時a1無效了*/
auto_ptr<A> a3(new A());
cout << "Exit create()
";
sink(a2);/*丟失所有權,會發現a2的釋放在sink函數中進行*/
cout << "Exit sink()
";
return 0;
}
auto_ptr淺析
auto_ptr是C++標準庫中(<utility>)為了解決資源泄漏的問題提供的一個智能指針類模板(注意:這只是一種簡單的智能指針)
auto_ptr的實現原理其實就是RAII,在構造的時候獲取資源,在析構的時候釋放資源,并進行相關指針操作的重載,使用起來就像普通的指針。
std::auto_ptr<ClassA> pa(new ClassA);
下面主要分析一下auto_ptr的幾個要注意的地方:
1,Transfer of Ownership
auto_ptr與boost庫中的share_ptr不同的,auto_ptr沒有考慮引用計數,因此一個對象只能由一個auto_ptr所擁有,在給其他auto_ptr賦值的時候,會轉移這種擁有關系。
2,從上可知由于在賦值,參數傳遞的時候會轉移所有權,因此不要輕易進行此類操作。
比如:std::auto_ptr<ClassA> pa(new ClassA());
bad_print(pa); //丟失了所有權
pa->...; //Error
3,使用auto_ptr作為成員變量,以避免資源泄漏。
為了防止資源泄漏,我們通常在構造函數中申請,析構函數中釋放,但是只有構造函數調用成功,析構函數才會被調用,換句話說,如果在構造函數中產生了異常,那么析構函數將不會調用,這樣就會造成資源泄漏的隱患。
比如,如果該類有2個成員變量,指向兩個資源,在構造函數中申請資源A成功,但申請資源B失敗,則構造函數失敗,那么析構函數不會被調用,那么資源A則泄漏。
為了解決這個問題,我們可以利用auto_ptr取代普通指針作為成員變量,這樣首先調用成功的成員變量的構造函數肯定會調用其析構函數,那么就可以避免資源泄漏問題。
4,不要誤用auto_ptr
1)auto_ptr不能共享所有權,即不要讓兩個auto_ptr指向同一個對象。
2)auto_ptr不能指向數組,因為auto_ptr在析構的時候只是調用delete,而數組應該要調用delete[]
3)auto_ptr只是一種簡單的智能指針,如有特殊需求,需要使用其他智能指針,比如share_ptr。
4)auto_ptr不能作為容器對象,STL容器中的元素經常要支持拷貝,賦值等操作,在這過程中auto_ptr會傳遞所有權,那么source與sink元素之間就不等價了。
總結
- 上一篇: 工业炉温度计算机控制系统,热处理工业炉计
- 下一篇: windows安装TeXstudio+M