總結四類智能指針

智能指針的理解

C++中的動態內存管理通過 new delete運算符來完成的:

new:在動態內存中爲對象分配空間並且返回一個指向該對象的指針,可對對象進行初始化;
delete:接受一個動態對象的指針,銷燬該對象,並釋放與之關聯的內存。

首先說一下爲什麼使用智能指針,存在以下幾種情形:

  1. 申請的空間在使用完 甚至程序結束時忘記釋放,這會造成了內存泄露;
  2. 有時在尚有指針引用內存的情況下 內存就被釋放了,這會產生非法內存的指針(野指針);

現有的智能指針有:auto_ptr、shared_ptr、weak_ptr、unique_ptr,其中第一個auto_ptr已被c++11棄用,後三個是c++11新支持的。
使用智能指針很大程度上可以避免上面的問題,因爲智能指針就是一個類,當超過了類的作用域,類會自動調用析構函數,析構函數會自動釋放資源。使用智能指針需要添加頭文件memory。

auto_ptr

c++98方案,c++11已經棄用,但是這裏還是要講一下:
auto_ptr採用所有權模式:

auto_ptr< string> p1 (new string ("I reigned lonely as a cloud.”));
auto_ptr<string> p2;
p2 = p1; //auto_ptr不會報錯.

此時不會報錯,p2剝奪了p1的所有權,但是當程序運行時訪問p1將會報錯。
所以auto_ptr的缺點是:存在潛在的內存崩潰問題!

unique_ptr

替換了auto_ptr,同樣採用了所有權模式。
unique_ptr實現獨佔式擁有或嚴格擁有概念,保證同一時間內只有一個智能指針可以指向該對象。它對於避免資源泄露(例如“以new創建對象後因爲發生異常而忘記調用delete”)特別有用。

unique_ptr<string> p3 (new string ("auto"));           //#4
unique_ptr<string> p4;                           //#5
p4 = p3;//此時會報錯!!

編譯器認爲p4=p3非法,避免了p3不再指向有效數據的問題。因此,unique_ptr比auto_ptr更安全。
另外unique_ptr還有更聰明的地方:當程序試圖將一個 unique_ptr 賦值給另一個時,如果源 unique_ptr 是個臨時右值,編譯器允許這麼做;如果源 unique_ptr 將存在一段時間,編譯器將禁止這麼做,比如:

unique_ptr<string> pu1(new string ("hello world"));
unique_ptr<string> pu2;
pu2 = pu1;                                          // #1 not allowed
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string ("You"));           // #2 allowed

其中#1留下懸掛的unique_ptr(pu1),這可能導致危害。而#2不會留下懸掛的unique_ptr,因爲它調用 unique_ptr 的構造函數,該構造函數創建的臨時對象在其所有權讓給 pu3 後就會被銷燬。這種隨情況而已的行爲表明,unique_ptr 優於允許兩種賦值的auto_ptr 。
注:如果確實想執行類似與#1的操作,要安全的重用這種指針,可給它賦新值。C++有一個標準庫函數std::move(),讓你能夠將一個unique_ptr賦給另一個。例如:

unique_ptr<string> ps1, ps2;
ps1 = demo("hello");
ps2 = move(ps1);
ps1 = demo("alexia");
cout << *ps2 << *ps1 << endl;

shared_ptr

shared_ptr實現共享式擁有概念。多個智能指針可以指向相同對象,該對象和其相關資源會在“最後一個引用被銷燬”時候釋放。從名字share就可以看出了資源可以被多個指針共享,它使用計數機制來表明資源被幾個指針共享。可以通過成員函數use_count()來查看資源的所有者個數。除了可以通過new來構造,還可以通過傳入auto_ptr, unique_ptr,weak_ptr來構造。當我們調用release()時,當前指針會釋放資源所有權,計數減一。當計數等於0時,資源會被釋放。
shared_ptr 是爲了解決 auto_ptr 在對象所有權上的侷限性(auto_ptr 是獨佔的), 在使用引用計數的機制上提供了可以共享所有權的智能指針。
c++11提供的這兩種智能指針( shared_ptr、unique_ptr)的區別在於管理底層指針的方式:

  • shared_ptr 允許多個指針指向同一個對象,weak_ptr是一個伴隨類,他是一種弱引用,指向shared_ptr所管理的對象。
  • unique_ptr則獨佔所指的對象
    類的成員函數:
  • use_count 返回引用計數的個數
  • unique 返回是否是獨佔所有權( use_count 爲 1)
  • swap 交換兩個 shared_ptr 對象(即交換所擁有的對象)
  • reset 放棄內部對象的所有權或擁有對象的變更, 會引起原有對象的引用計數的減少
  • get 返回內部對象(指針), 由於已經重載了()方法, 因此和直接使用對象是一樣的.如 shared_ptr sp(new int(1)); sp 與 sp.get()是等價的

weak_ptr

weak_ptr 是一種不控制對象生命週期的智能指針, 它指向一個 shared_ptr 管理的對象. 進行該對象的內存管理的是那個強引用的 shared_ptr. weak_ptr只是提供了對管理對象的一個訪問手段。weak_ptr 設計的目的是爲配合 shared_ptr 而引入的一種智能指針來協助 shared_ptr 工作, 它只可以從一個 shared_ptr 或另一個 weak_ptr 對象構造, 它的構造和析構不會引起引用記數的增加或減少。weak_ptr是用來解決shared_ptr相互引用時的死鎖問題,如果說兩個shared_ptr相互引用,那麼這兩個指針的引用計數永遠不可能下降爲0,資源永遠不會釋放。它是對對象的一種弱引用,不會增加對象的引用計數,和shared_ptr之間可以相互轉化,shared_ptr可以直接賦值給它,它可以通過調用lock函數來獲得shared_ptr。

class B;
class A
{
public:
shared_ptr<B> pb_;
~A()
{
cout<<"A delete\n";
}
};
class B
{
public:
shared_ptr<A> pa_;
~B()
{
cout<<"B delete\n";
}
};
void fun()
{
shared_ptr<B> pb(new B());
shared_ptr<A> pa(new A());
pb->pa_ = pa;
pa->pb_ = pb;
cout<<pb.use_count()<<endl;
cout<<pa.use_count()<<endl;
}
int main()
{
fun();
return 0;
}

可以看到fun函數中pa ,pb之間互相引用,兩個資源的引用計數爲2,當要跳出函數時,智能指針pa,pb析構時兩個資源引用計數會減一,但是兩者引用計數還是爲1,導致跳出函數時資源沒有被釋放(A B的析構函數沒有被調用),如果把其中一個改爲weak_ptr就可以了,我們把類A裏面的shared_ptr pb_; 改爲weak_ptr pb_; 運行結果如下,這樣的話,資源B的引用開始就只有1,當pb析構時,B的計數變爲0,B得到釋放,B釋放的同時也會使A的計數減一,同時pa析構時使A的計數減一,那麼A的計數爲0,A得到釋放。
注意的是我們不能通過weak_ptr直接訪問對象的方法,比如B對象中有一個方法print(),我們不能這樣訪問,pa->pb_->print(); 英文pb_是一個weak_ptr,應該先把它轉化爲shared_ptr,如:shared_ptr p = pa->pb_.lock(); p->print();

NOTE

  • 由於unique_ptr和shared_ptr是C++11新增的智能指針,所以編譯時需要添加支持C++11的參數
    如g++ xx.cpp -o xx -std=c++11
  • 智能指針在std域裏,需要添加名稱空間using namespace std或者在s使用智能指針前加std::shared_ptr
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章