shared_ptr和weak_ptr以及循環引用問題


shared_ptr的出現:

  使用裸指針對堆對象(動態內存)進行管理是極其容易出現問題的。例如:忘記釋放內存造成的內存泄漏、尚有指針引用內存的情況下釋放了該內存等等的問題。

  為此能夠更加智能地保留或者釋放堆(動態)對象,標准庫以及boost庫提供了智能指針。智能指針負責自動釋放所指向的對象。智能指針的使用和普通指針類似,解引用一個智能指針返回它指的對象。

  shared_ptr:允許多個指針指向同一個對象。

shared_ptr用法:

#include <iostream>
#include <memory>

using namespace std;

class Test
{
public:
    Test(string s):_str(s)
    {
        cout << "Test create" << endl;
    }

    ~Test()
    {
        cout << "Test delete" << endl;
    }
    string &getStr()
    {
        return _str;
    }
    void setStr(string s)
    {                                                                                                                                        
        _str = s;
    }

    void print()
    {
        cout << _str << endl;
    }

private:
    string _str;
};

 

 

int main()
{
    shared_ptr<Test> p1 = make_shared<Test>("pTest1");
    shared_ptr<Test> p2 = make_shared<Test>("pTest2");
    shared_ptr<Test> p3 = make_shared<Test>("pTest3");
    p3 = p1;
    p2 = p1;
    cout << p3.use_count() << endl;
    cout << p1.use_count() << endl;
    return 0;    
}

將p1智能指針賦值給了p3背后發生的事情:p3所指向的對象引用計數-1后為0,釋放p3所指向的對象。p1的引用計數+1。

問題:通過p3可以拿到所指向的引用計數值為3,通過p1也能拿到所指向的引用計數為3。是否p1智能指針對象和p3智能指針對象都保存了一份引用計數值呢?

參考《c++ primer第五版》P402頁所給出的解釋:

到底是用一個計數器還是其他數據結構來記錄有多少指針共享對象, 完全由標准庫的具體實現來決定。關鍵是智能指針類能記錄有多少個shared_ptr指向相同的對象,並能在恰當的時候自動釋放對象。

 

雖然智能指針能自動釋放內存,但是使用不當同樣會導致內存泄漏。

shared_ptr的循環引用問題:

循環引用問題模型:

問題描述:

如果有一個類A和類B,其數據成員是一個shared_ptr指向彼此。那么此時類A和類B的引用計數ref為1。如果此時又有兩個智能指針分別指向A和B,那么此時類A和類B的引用計數為2,當這兩個智能指針離開其作用域的時候ref減為1,但並不會釋放智能指針所指向的對象。會造成內存泄漏。

#include <iostream>
#include <memory>
using namespace std;

class B;
class A
{
public:
    ~A()
    {
        cout << "A delete" << endl;
    }
    shared_ptr<B> ptr;
};

class B
{
public:
    ~B()
    {
        cout << "B delete" << endl;
    }
    shared_ptr<A> ptr;
};

int main()
{
    while(1)
    {
        shared_ptr<A> pa(new A());   //A對象的引用計數ref=1
        shared_ptr<B> pb(new B());   //B對象的引用計數ref=1
        pa->ptr = pb;    //B對象的引用計數ref=2
        pb->ptr = pa;    //A對象的引用計數ref=2
    }
     //離開作用域后,雖然pa和pb智能指針對象釋放了,但由於其所指對象的引用計數為1而未被釋放,故造成內存泄漏。
}  

weak_ptr的出現:

  為了解決循環引用的問題,出現了弱引用的weak_ptr。weak_ptr指向對象並不會對引用計數+1。weak_ptr不對其所指的對象進行內存資源的管理。解決循環引用的方法就是將shared_ptr的數據成員改為weak_ptr。

weak_ptr的用法:

  當創建一個weak_ptr時,要用一個shared_ptr來初始化它:

shared_ptr<int> p = make_shared<int>(111);
weak_ptr wp(p);

  因為是弱引用,創建wp不會改變p的引用計數。有可能weak_ptr所指向的對象不存在了,因此無法直接通過weak_ptr指針訪問其所指向的對象,應該通過調用lock()方法將weak_ptr提升為一個shared_ptr,再訪問其所指向的對象。如果提升失敗那么指向的對象已被釋放。

if (shared_ptr<int> p = wp.lock())
{
    //.....  
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM