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