溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C++智能指針怎么創(chuàng)建和使用

發(fā)布時間:2022-08-27 09:25:26 來源:億速云 閱讀:158 作者:iii 欄目:開發(fā)技術(shù)

今天小編給大家分享一下C++智能指針怎么創(chuàng)建和使用的相關(guān)知識點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

    優(yōu)缺點(diǎn):

    • 智能指針由原始指針的封裝,優(yōu)點(diǎn)是可以自動分配內(nèi)存,不用擔(dān)心內(nèi)存泄漏問題。

    • 用于解決獨(dú)占/共享所有權(quán)指針的釋放,傳輸?shù)葐栴}。

    • 但是沒有原始指針方便。

    一. unique_ptr獨(dú)占指針

    特點(diǎn)

    都是圍繞獨(dú)占展開

    特點(diǎn)一: 如其名,獨(dú)占。也就是說同一個內(nèi)存空間同時只能有一個指針來管理。

    int* pi = new int(10); //利用傳統(tǒng)指針在堆區(qū)開辟一個存放整數(shù)的區(qū)域
    std::unique_ptr<int> u_pi_01{pi};//通過傳統(tǒng)指針創(chuàng)建智能指針
    std::unique_ptr<int> u_pi_02{pi};//有意讓兩個獨(dú)占指針同時指向同一個內(nèi)存區(qū)域

    這么寫編譯器不會報錯,但運(yùn)行時會提示 error:double free detected in tcache 2

    這也就印證了第一個特點(diǎn),一個內(nèi)存區(qū)域只能由一個指針管理。

    特點(diǎn)二: 當(dāng)指針超出作用域時,內(nèi)存自動釋放

    //由于指針的本質(zhì)也是變量,離開作用范圍就會自動釋放
    //因此我們需要通過在外部創(chuàng)建變量來保存指針?biāo)4娴牡刂?
    int* external_pi_1;//用于存儲傳統(tǒng)指針的地址
    int* external_pi_2;//用于存儲智能指針的地址
     {
        int *pi = new int(10);//使用傳統(tǒng)指針在堆區(qū)開辟內(nèi)存存儲整形
        external_pi_1 = pi;   
        std::unique_ptr<int> u_pi{new int(10)};//使用智能指針在堆區(qū)開辟內(nèi)存存儲
        external_pi_2 = u_pi.get();
        std::cout << *external_pi_1 << std::endl;//輸出10
        std::cout << *external_pi_2 << std::endl;//輸出10
     }
    std::cout << *external_pi_1 << std::endl;//輸出10
    std::cout << *external_pi_2 << std::endl;//輸出0

    可見傳統(tǒng)指針在局部作用域中開辟的內(nèi)存在外部同樣可以訪問,也就是說我們使用傳統(tǒng)指針開辟內(nèi)存之后在離開作用域時需要加上釋放內(nèi)存的操作,不然會造成內(nèi)存泄漏。

    而智能指針我們不需要手動釋放內(nèi)存,在離開作用域后會自動釋放。

    特點(diǎn)三:由于特點(diǎn)一,修改指針不可以copy ,只能Move(轉(zhuǎn)移歸屬權(quán))

    std::unique_ptr<int> u_pi1 = std::make_unique<int>(10);
    //std::unique_ptr<int> u_pi2 = u_pi1;//嘗試用拷貝的方式共享內(nèi)存,error:可別忘了這是獨(dú)占指針
    std::unique_ptr<int> u_pi2 = move(u_pi1);使用move方法轉(zhuǎn)移內(nèi)存擁有權(quán)。

    也就是說,通過move函數(shù),把指針u_pi1所指內(nèi)存中的值掏空,然后安到指針u_pi2所指的內(nèi)存上。

    創(chuàng)建方式

    方式一: 通過已有的傳統(tǒng)指針創(chuàng)建

    int* pi = new int(10); //使用傳統(tǒng)指針在堆區(qū)開辟一個空間
    std::unique_ptr<int> u_pi{pi};//利用創(chuàng)通指針創(chuàng)建智能指針

    方式二: 通過new方法創(chuàng)建

    std::unique_ptr<int> u_pi{new int(10)};

    方式三: 通過std :: make_unique創(chuàng)建

    std::unipue_ptr<int> u_pi = std::make_unique<int>(10);

    傳遞方式

    方式一: 通過move(),轉(zhuǎn)移擁有權(quán).

    void show(std::unique_ptr<int> u_pi)
    {
        std::cout<<*u_pi<<std::endl;
    }
    void test()
    {
        std::unique_ptr<int> u_pi{new int(10)};
        show(move(u_pi)); //通過move轉(zhuǎn)移擁有權(quán)
    }

    注意:將指針的擁有權(quán)轉(zhuǎn)入函數(shù)中后,在原作用域指針將被釋放,而該指針將在函數(shù)調(diào)用結(jié)束時釋放。也就是說,將智能指針以move的形式傳入函數(shù)后,在原作用域不能再使用該指針。

    方式二: 通過引用傳遞

    void show(const std::unique_ptr<int> &u_pi)//加cosnt 不是不能改變指向的值,不能改變指針的指向
    {
        std::cout << *u_pi << std::endl;
        //u_pi.reset();加了const所以不能清空
    }
    void test()
    {
        std::unique_ptr<int> u_pi{new int(10)};
        show(u_pi);
    }

    注意: 將指針以引用的方式傳入函數(shù),那么該指針在原作用域依然存活,并可以和所調(diào)用函數(shù)共同操作該內(nèi)存空間數(shù)據(jù)。

    方式三: 鏈?zhǔn)絺鬟f

    std::unique_ptr<Person> get_unique(std::string str)
    {
        std::unique_ptr<Person> u_pi{new Person(str)};
        return u_pi;
    }
    void test()
    {
        get_unique("hua")->show();//鏈?zhǔn)?
    }

    簡單使用

    • 通過get()獲取地址

    • 可以通過->調(diào)用成員函數(shù)

    • 可以通過*調(diào)用解引用

    • 通過reset()清空指針

    class Person
    {
    public:
        Perosn(std::string name):m_name(name){};
        void show()
        {
            std::cout<<"name is "<<m_name<<std::endl;
        }
    private:
        std::string m_name;   
    };
    int main()
    {
    std::unique_ptr<Person> u_p{new Person("kimi")}; //用自定義類型創(chuàng)建
    u_p->show();//可以通過->調(diào)用函數(shù)
    (*u_p).show();//通過*解引用
    std::cout<<u_p.get()<<std::endl;//通過get()獲取地址
    u_p.reset();//清空指針
    return 0;
    }

    隱藏危險

    用已有指針創(chuàng)建時,沒有及時清空傳統(tǒng)指針,導(dǎo)致同時有兩個指針指向這塊已經(jīng)被“獨(dú)占”的區(qū)域。

    int* pi = new int(10);
    std::unique_ptr<int> u_pi{pi};//使用傳統(tǒng)指針創(chuàng)建,上式開辟的區(qū)域被獨(dú)占
    *pi = 20; //沒有及時清空,依然可以通過獨(dú)占指針以外的方式修改內(nèi)存

    二. shared_ptr 計數(shù)指針

    特點(diǎn)

    特點(diǎn)一: 可以通過copy共享內(nèi)存。

    std::shared_ptr<int> u_pi_1{new int(10)};
    std::shared_ptr<int> u_pi_2 = u_pi_1;//通過復(fù)制拷貝

    特點(diǎn)二: 通過use_count();來查看計數(shù) ,copy 計數(shù)加一,銷毀計數(shù)減一。

    std::shanred_ptr<int> s_pi{new int(10)}; //s_pi.use_count() == 1
    std::shanred_ptr<int> s_pi_copy = s_pi;  //s_pi.use_count() == 2
    s_pi = nullptr;//清空指針                 //s_pi_copy.use_count() == 1

    特點(diǎn)三: 無論多少指針,都同用一份數(shù)據(jù),因而同一份數(shù)據(jù)的use_count()一致。

    std::shared_ptr<int> s_pi{new int(10)}; //s_pi.use_count() == 1
    std::shared_ptr<int> s_pi_2 = s_pi;     //s_pi.use_count() == 2
    std::shared_ptr<int> s_pi_3 = s_pi_2;   //s_pi.use_count() == 3
    s_pi_2 = bullptr; //清空2指針            //s_pi.use_count() == 2

    傳遞方式

    • 本質(zhì)不變,在函數(shù)調(diào)用中,因?yàn)楸旧碇С謴?fù)制操作,所以不用加move可以直接傳遞。

    • 并且在傳遞到函數(shù)中,use_count() 會增加,并在函數(shù)銷毀時候還原。

    • 在函數(shù)中修改指向的值,在外部的指針指向的值也會改變。

    • 使用引用傳遞,則在傳遞到函數(shù)中時,計數(shù)不會增加。

    void get_use_1(std::shared_ptr<int> s_pi)
    {
        std::cout << s_pi.use_count() << std::endl;
    }
    void get_use_2(std::shared_ptr<int>& s_pi)
    {
        std::cout << s_pi.use_count() << std::endl;
    }
    void test()
    {
        std::shared_ptr<int> s_pi{new int(10)};
        std::cout << s_pi.use_count() << std::endl;
        get_use_1(s_pi);//在函數(shù)中計數(shù)會增加,但隨著函數(shù)銷毀,計數(shù)復(fù)原
        get_use_2(s_pi);//以引用方式傳入,指針還是那個指針,計數(shù)不會增加
    }

    輸出:1 2 1

    隱藏危險

    share_ptr帶來的循環(huán)依賴問題

    class Person
    {
    public:
        void set_friend(share_ptr<Person> p)
     
        _friend = p;
    private:
        share_ptr<Person> _frient;
    };
    int main()
    {
       share_ptr<Person> p1 = make_shared("P1");
       share_ptr<Person> p2 = make_shared("P2");
        p1->set_friend(P2);
        p2->set_friend(P1);//造成循環(huán)依賴,在main中的話,不會執(zhí)行析構(gòu)
    }

    解決:將_friend屬性改為weak_ptr 。

    三. weak_ptr

    weak_ptr 是一個不需要所有權(quán)的指針,所以我們可以通過用weak_ptr來聲明屬性,解決循環(huán)依賴

    class Person
    {
    public:
        void set_friend(share_ptr<Person> p)
        _friend = p;
    private:
        weak_ptr<Person> _frient;//使用weak_ptr解決循環(huán)依賴
    };
    int main()
    {
        share_ptr<Person> p1 = make_shared("P1");
        share_ptr<Person> p2 = make_shared("P2");
        p1->set_friend(P2);
        p2->set_friend(P1);
    }

    可以通過lock()來將weak_pte升級為shared_ptr;

    std::weak_ptr<Person> w_pi{new Person("hua")};
    std::shared_ptr <Person> s_pi2 = w_pi.lock();

    以上就是“C++智能指針怎么創(chuàng)建和使用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學(xué)習(xí)更多的知識,請關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    c++
    AI