STL里的四種智能指針 auto_ptr、scoped_ptr、shared_ptr、weak_ptr


基于安全考慮:

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)移。


image.png
//智能指針的創(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)引用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容