基于安全考慮:
auto_ptr< string> ps (new string ("I reigned lonely as a cloud.”);
auto_ptr<string> vocation;
vocaticn = ps;
因?yàn)槌绦驅(qū)⒃噲D刪除同一個(gè)對(duì)象兩次,要避免這種問題,方法有多種:
(1)定義賦值運(yùn)算符,使之執(zhí)行深復(fù)制。這樣兩個(gè)指針將指向不同的對(duì)象,其中的一個(gè)對(duì)象是另一個(gè)對(duì)象的副本,缺點(diǎn)是浪費(fèi)空間,所以智能指針都未采用此方案。
(2)建立所有權(quán)概念。對(duì)于特定的對(duì)象,只能有一個(gè)智能指針可擁有,這樣只有擁有對(duì)象的智能指針的析構(gòu)函數(shù)會(huì)刪除該對(duì)象。然后讓賦值操作轉(zhuǎn)讓所有權(quán)。這就是用于auto_ptr和unique_ptr 的策略,但unique_ptr的策略更嚴(yán)格。
(3)創(chuàng)建智能更高的指針,跟蹤引用特定對(duì)象的智能指針數(shù)。這稱為引用計(jì)數(shù)。例如,賦值時(shí),計(jì)數(shù)將加1,而指針過期時(shí),計(jì)數(shù)將減1,。當(dāng)減為0時(shí)才調(diào)用delete。這是shared_ptr采用的策略。
1. unique_ptr:(防止析構(gòu)一塊內(nèi)存多次)
unique_ptr由C++11引入,旨在替代不安全的auto_ptr。
unique_ptr不共享它的所管理的對(duì)象。它無法復(fù)制到其他unique_ptr,無法通過值傳遞到函數(shù),也無法用于需要副本的任何標(biāo)準(zhǔn)模板庫(kù) (STL)算法。只能移動(dòng) unique_ptr,即對(duì)資源管理權(quán)限可以實(shí)現(xiàn)轉(zhuǎn)移。

//智能指針的創(chuàng)建
unique_ptr<int> u_i; //創(chuàng)建空智能指針
u_i.reset(new int(3)); //"綁定”動(dòng)態(tài)對(duì)象
unique_ptr<int> u_i2(new int(4));//創(chuàng)建時(shí)指定動(dòng)態(tài)對(duì)象
unique_ptr<T,D> u(d); //創(chuàng)建空unique_ptr,執(zhí)行類型為T的對(duì)象,用類型為D的對(duì)象d來替代默認(rèn)的刪除器delete
//所有權(quán)的變化
int *p_i = u_i2.release(); //釋放所有權(quán)
unique_ptr<string> u_s(new string("abc"));
unique_ptr<string> u_s2 = std::move(u_s); //所有權(quán)轉(zhuǎn)移(通過移動(dòng)語義),u_s所有權(quán)轉(zhuǎn)移后,變成“空指針”
u_s2.reset(u_s.release());//所有權(quán)轉(zhuǎn)移
u_s2=nullptr;//顯式銷毀所指對(duì)象,同時(shí)智能指針變?yōu)榭罩羔?。與u_s2.reset()等價(jià)
2.auto_ptr:為什么不用它而用unique_ptr(廢柴版unique_ptr)
使用unique_ptr時(shí)編譯出錯(cuò),與auto_ptr一樣,unique_ptr也采用所有權(quán)模型,但在使用unique_ptr時(shí),程序不會(huì)等到運(yùn)行階段崩潰,而在編譯期因下述代碼行出現(xiàn)錯(cuò)誤。一句話總結(jié)就是:避免因潛在的內(nèi)存問題導(dǎo)致程序崩潰。
int main()
{
auto_ptr<string> films[5] ={
auto_ptr<string> (new string("Fowl Balls")),
auto_ptr<string> (new string("Duck Walks")),
auto_ptr<string> (new string("Chicken Runs")),
auto_ptr<string> (new string("Turkey Errors"))
};
auto_ptr<string> pwin;
pwin = films[2];
// films[2] loses ownership. 將所有權(quán)從films[2]轉(zhuǎn)讓給pwin,此時(shí)films[2]不再引用該字符串從而變成空指針
for(int i = 0; i < 4; ++i)
{
cout << *films[i] << endl;
}
return 0;
}
從上面可見,unique_ptr比auto_ptr更加安全,因?yàn)閍uto_ptr有拷貝語義,拷貝后原象變得無效,再次訪問原對(duì)象時(shí)會(huì)導(dǎo)致程序崩潰;unique_ptr則禁止了拷貝語義,但提供了移動(dòng)語義,即可以使用std::move()進(jìn)行控制權(quán)限的轉(zhuǎn)移
unique_ptr<string> upt(new string("lvlv"));
unique_ptr<string> upt1(upt); //編譯出錯(cuò),已禁止拷貝
unique_ptr<string> upt1=upt; //編譯出錯(cuò),已禁止拷貝
unique_ptr<string> upt1=std::move(upt); //控制權(quán)限轉(zhuǎn)移,正確的寫法
auto_ptr<string> apt(new string("lvlv"));
auto_ptr<string> apt1(apt); //編譯通過
auto_ptr<string> apt1=apt; //編譯通過
- 使用shared_ptr時(shí)運(yùn)行正常,因?yàn)閟hared_ptr采用引用計(jì)數(shù),pwin和films[2]都指向同一塊內(nèi)存,在釋放空間時(shí)因?yàn)槭孪纫袛嘁糜?jì)數(shù)值的大小因此不會(huì)出現(xiàn)多次刪除一個(gè)對(duì)象的錯(cuò)誤。
3. shared_ptr
參看內(nèi)存垃圾管理(智能指針)
4. weak_ptr(shared_ptr的小跟班)
被設(shè)計(jì)為與shared_ptr共同工作,可以從一個(gè)shared_ptr或者另一個(gè)weak_ptr對(duì)象構(gòu)造而來。
為什么用weak_ptr就可以解決循環(huán)引用的問題?簡(jiǎn)單點(diǎn)的來說:weak_ptr的構(gòu)造和析構(gòu)不會(huì)引起引用計(jì)數(shù)的增加或減少。weak_ptr必須與shared_ptr配合使用,不能單獨(dú)使用。
template<typename T>
struct ListNode{
T _value;
weak_ptr<ListNode> _prev;
weak_ptr<ListNode> _next;
ListNode(const T & value)
:_value(value)
,_prev(NULL)
,_next(NULL){}
~ListNode(){
std::cout<<"~ListNode()"<<std::endl;
}
};
void TestWeekPtr(){
std::shared_ptr<ListNode<int>> sp1(new ListNode<int>(10));
std::shared_ptr<ListNode<int>> sp2(new ListNode<int>(20));
sp1->_next = sp2;
sp2->_prev = sp1;
std::cout<<sp1.use_count()<<std::endl;
std::cout<<sp2.use_count()<<std::endl;
}
5. scoped_ptr (防拷貝賦值智能指針)
循環(huán)引用:
一般來講,解除這種循環(huán)引用有下面三種可行的方法:
(1)當(dāng)只剩下最后一個(gè)引用的時(shí)候需要手動(dòng)打破循環(huán)引用釋放對(duì)象。
(2)當(dāng)parent的生存期超過children的生存期的時(shí)候,children改為使用一個(gè)普通指針指向parent。
(3)使用弱引用的智能指針打破這種循環(huán)引用。
雖然這三種方法都可行,但方法1和方法2都需要程序員手動(dòng)控制,麻煩且容易出錯(cuò)。這里主要介紹一下第三種方法,使用弱引用的智能指針std:weak_ptr來打破循環(huán)引用。