C++深入理解(14)------智能指針(讀書筆記)

這裏只是介紹智能用法和注意事項,簡單介紹其原理,如果想深入瞭解其實現的同學可以繞道。

  1. 爲什麼用智能指針:在編寫服務器代碼的時候,經常會出現new對象的情況,比如遊戲服務器中,一個新玩家上線了,需要new player,放到mapAllPlayer中,直到玩家下線才能刪除。但是在玩家下線時,可能調用了map.erese(),忘記delete指針,或者都忘記了,就會造成內存泄漏,也就是內存不斷瘋長,直到程序崩掉。
  2. 智能指針的分類:auto_ptr,unique_ptr,shared_ptr.三種,但是auto_ptr因其缺點,已經被棄用,最好不要使用他,後兩者是C++11爲我們提供的新型智能指針。
  3. 使用智能指針:
    要想使用智能指針,首先先了解下智能指針模板的定義,實際上智能指針就是封裝好的三個類。
    template<class X> class auto_ptr
    {
        public:
            explicit auto_ptr(X* p = 0);
        ...
    }
    如上邊的代碼,每種指針都包含這種類型的構造函數,explicit指構造函數不能隱身轉化。也就是說下面的代碼是不允許的
    //正常使用
    class A;        //已定義好的class A、
    std::auto_ptr<A> ps(new A());
    std::shared_ptr<A> ps(new A());
    std::unique_ptr<A> ps(new A());
    
    
    //錯誤使用:
    shared_ptr<double> pd;
    double* p_reg = new double;
    pd = p_reg;     //這是不允許的,這種實際上是在調用pd的賦值運算符,不允許將非智能指針賦值給智能指針
    
    shared_ptr<double> pshared = p_reg;  //不允許,這是在隱式調用構造函數,explicit不允許隱身調用。
    shared_ptr<double> pshared(p_reg);   //允許的,正常調用構造函數
    
    pd = shared_ptr<double>(p_reg);  //這是允許的,實際上這是先創建一個智能指針的臨時變量,
                                     //然後調用賦值運算符,將智能指針賦值給智能指針。
    賦值之後,我們就可以使用類似ps->的用法,使用智能指針了。
    但是一定要避免一種情況,使用臨時變量去構造智能指針,如
    string str("hello,wordl");
    shared_ptr<string> ptr(&str); 這是絕對拒絕的
  4. 關於幾種指針的區別,和注意事項:
    這裏必須強調一句,智能指針也不是絕對智能的,必須合理使用。
    說一種特殊情況:在調用複製構造函數時,常常遇到指針賦值,如下面代碼:
    A::A()
    {
        m_pstr = new string();
    }
    
    A::A(const A& a)
    {
        m_pstr = a.m_pstr;
    }
    這種情況就是不負責任的表現,兩個對象的成員變量m_pstr都指向同一個地址,當一個成員變量被銷燬會,另一個對象的該成員變量也會變成無效值。導致程序崩潰等。一般的做法是進行深拷貝,也就是在複製構造函數中,去new新變量。而在智能指針中是另一種處理方式:
    a.建立所有權關係:對一個對象,只有一個智能指針能擁有他,當賦值操作時,轉讓所有權,舊的指針不在有意義。這是auto_ptr和unique_ptr的策略。
    b.創建智能更高的指針,跟蹤引用該對象的智能指針數,稱爲引用計數,賦值時,引用數加一,指針過期時,引用次數減一,最後一個指針過期時,該對象被銷燬。

    光說概念,可能不懂什麼是所有權,什麼是引用,下面寫一段簡單代碼,請看完,很有用:
    #include <iostream>
    #include <memory>
    #include <string>
    
    using namespace std;
    
    int main()
    {
    	auto_ptr<double> pSrc(new double(2.0));
    	cout << *pSrc << endl;
    
    	auto_ptr<double> pNex = pSrc;
    	cout << *pNex << endl;
    	cout << *pSrc << endl;
    
    	return 1;
    }
    這個代碼將pNex = pSrc,對象double(2.0)的所有權給了pNex,但是pSrc變爲了懸掛指針,所以cout<<*pSrc,就會崩潰,這也是C++11摒棄了auto_prt的原因,而unique_ptr雖然也是所有權的策略,但是其根本不允許此類的賦值操作。編譯器直接報錯。但是編譯器如果檢測到用一個臨時變量去賦值unique_ptr時,是允許的:如一個函數:
     
    unique<int> func()
    { 
        unique<int> *p(new int);  
        return p;
    }    
    
    unique_ptr<int> ps = func()   //是允許的,因爲其不會造成任何懸掛指針,返回的臨時指針及時被銷燬了
    
    //使用std::move函數可以將所有權賦給新的一個,但是還是不安全的
    unique_ptr<int> ps1;
    ps1 = std::move(ps);  //此時ps變爲懸掛指針,必須賦新值纔可以使用
    
    

    如果非要進行賦值操作。那麼怎麼辦,就要用到shared_ptr,他允許賦值,會增加一次引用,將上面代碼的auto改爲shared就可以解決崩潰問題。
  5. 如何選擇智能指針:
    要根據需求,如果該指針引用的對象要參與賦值操作,需要使用shared_ptr,如果不參與賦值,則可以使用unique_ptr.最好不要使用auto_ptr指針。

    還有一點需要注意:如果不是用new出來的指針,三個智能指針都是不能使用的,unique支持new[],其他兩個不支持。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章