為什么要使用智能指針:
智能指針的作用是管理一個指針,因為存在以下這種情況:申請的空間在函數(shù)結(jié)束時忘記釋放,造成內(nèi)存泄漏。使用智能指針可以很大程度上的避免這個問題,因為智能指針就是一個類,當(dāng)超出了類的作用域是,類會自動調(diào)用析構(gòu)函數(shù),析構(gòu)函數(shù)會自動釋放資源。所以智能指針的作用原理就是在函數(shù)結(jié)束時自動釋放內(nèi)存空間,不需要手動釋放內(nèi)存空間。
1. auto_ptr(c++98的方案,cpp11已經(jīng)拋棄)
采用所有權(quán)模式。
auto_ptr< string> p1 (new string ("I reigned lonely as a cloud.”));
auto_ptr<string> p2;
p2 = p1; //auto_ptr不會報錯.
此時不會報錯,p2剝奪了p1的所有權(quán),但是當(dāng)程序運行時訪問p1將會報錯。所以auto_ptr的缺點是:存在潛在的內(nèi)存崩潰問題!
2. unique_ptr(替換auto_ptr)
unique_ptr實現(xiàn)獨占式擁有或嚴格擁有概念,保證同一時間內(nèi)只有一個智能指針可以指向該對象。它對于避免資源泄露(例如“以new創(chuàng)建對象后因為發(fā)生異常而忘記調(diào)用delete”)特別有用。
unique_ptr<string> p3 (new string ("auto")); //#4
unique_ptr<string> p4; //#5
p4 = p3;//此時會報錯??!
當(dāng)程序試圖將一個 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),這可能導(dǎo)致危害。而#2不會留下懸掛的unique_ptr,因為它調(diào)用 unique_ptr 的構(gòu)造函數(shù),該構(gòu)造函數(shù)創(chuàng)建的臨時對象在其所有權(quán)讓給 pu3 后就會被銷毀。這種隨情況而已的行為表明,unique_ptr 優(yōu)于允許兩種賦值的auto_ptr 。
如果確實想執(zhí)行類似與#1的操作,要安全的重用這種指針,可給它賦新值。C++有一個標(biāo)準(zhǔn)庫函數(shù)std::move(),讓你能夠?qū)⒁粋€unique_ptr賦給另一個。例如:
unique_ptr<string> ps1, ps2;
ps1 = demo("hello");
ps2 = move(ps1);
ps1 = demo("alexia");
cout << *ps2 << *ps1 << endl;
3. shared_ptr
shared_ptr實現(xiàn)共享式擁有概念。多個智能指針可以指向相同對象,該對象和其相關(guān)資源會在“最后一個引用被銷毀”時候釋放。除了可以通過new來構(gòu)造,還可以通過傳入auto_ptr, unique_ptr,weak_ptr來構(gòu)造。當(dāng)我們調(diào)用release()時,當(dāng)前指針會釋放資源所有權(quán),計數(shù)減一。當(dāng)計數(shù)等于0時,資源會被釋放。
成員函數(shù):
- use_count 返回引用計數(shù)的個數(shù)
- unique 返回是否是獨占所有權(quán)( use_count 為 1)
- swap 交換兩個 shared_ptr 對象(即交換所擁有的對象)
- reset 放棄內(nèi)部對象的所有權(quán)或擁有對象的變更, 會引起原有對象的引用計數(shù)的減少
- get 返回內(nèi)部對象(指針), 由于已經(jīng)重載了()方法, 因此和直接使用對象是一樣的.如 shared_ptr<int> sp(new int(1)); sp 與 sp.get()是等價的
4. weak_ptr
weak_ptr 是一種不控制對象生命周期的智能指針, 它指向一個 shared_ptr 管理的對象. 進行該對象的內(nèi)存管理的是那個強引用的 shared_ptr. weak_ptr只是提供了對管理對象的一個訪問手段。weak_ptr 設(shè)計的目的是為配合 shared_ptr 而引入的一種智能指針來協(xié)助 shared_ptr 工作, 它只可以從一個 shared_ptr 或另一個 weak_ptr 對象構(gòu)造, 它的構(gòu)造和析構(gòu)不會引起引用記數(shù)的增加或減少。weak_ptr是用來解決shared_ptr相互引用時的死鎖問題,如果說兩個shared_ptr相互引用,那么這兩個指針的引用計數(shù)永遠不可能下降為0,資源永遠不會釋放。它是對對象的一種弱引用,不會增加對象的引用計數(shù),和shared_ptr之間可以相互轉(zhuǎn)化,shared_ptr可以直接賦值給它,它可以通過調(diào)用lock函數(shù)來獲得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;
}
把其中一個改為weak_ptr就可以了。
注意的是我們不能通過weak_ptr直接訪問對象的方法,比如B對象中有一個方法print(),我們不能這樣訪問,pa->pb_->print(); 英文pb_是一個weak_ptr,應(yīng)該先把它轉(zhuǎn)化為shared_ptr,如:shared_ptr p = pa->pb_.lock(); p->print();
野指針
野指針就是指向一個已刪除的對象或者未申請訪問受限內(nèi)存區(qū)域的指針。
智能指針有沒有內(nèi)存泄露的情況
有, 當(dāng)兩個對象相互使用一個shared_ptr成員變量指向?qū)Ψ?,會造成循環(huán)引用,使引用計數(shù)失效,從而導(dǎo)致內(nèi)存泄漏。
解決方式:weak_ptr弱指針。