1. 程式人生 > >C++ -- 智慧指標( C++11與boost庫的智慧指標及其使用)

C++ -- 智慧指標( C++11與boost庫的智慧指標及其使用)

1 智慧指標的引入

2.但是有時候,我們new了,也delete了,但是還會出現問題。例如在new和delete之間呼叫了某個拋異常的函式,就有可能導致沒有執行delete。

例如:fun2裡面使用了new[],最後也使用了delete[],看著沒有問題,但是在new和delete之間呼叫了fun1,而fun1裡面拋了異常,但是在fun2的delete之前並沒有捕獲,就會導致delete沒有執行,仍然會有記憶體洩露的問題。

void fun1()
{
      throw int(11);
}
void fun2()
{
      int* p = new int[10000];
      fun1();
      delete[] p;
}

int main()
{
      try
      {
           fun2();
      }
      catch (int& e)
      {
           cout << "捕獲" << endl;
      }
      system("pause");
      return 0;
}

果想要解決上面的問題,有一種方法:就是如果發現delete之前發現某個函式丟擲了異常,就在delete之前捕獲這個異常,並且在catch語句裡面進行資源的釋放,並且可以再將這個異常重新丟擲。

void fun2()
{
      int* p = new int[10000];
      try
      {
           fun1();
      }
      catch(int& e)
      {
           delete[] p;
           cout << "重新丟擲" << endl;
           throw;
      }
      delete[] p;
}

但是這種方法寫著比較繁瑣。
4.智慧指標就有上面的作用,能夠自動的管理指標所指向的動態資源的釋放。它不僅有著RAII的思想還能夠像指標一樣。(RAII:分配資源即初始化,即建構函式分配資源和初始化資源,在解構函式清理資源。像指標一樣:能夠解引用)。
5.智慧指標實質上就是一個模板類,成員變數包含一個任意型別的指標,建構函式獲得資源並初始化,解構函式清理資源。
注意:智慧指標只能管理動態開闢的空間。

2 智慧指標的發展史

智慧指標都具有RAII的思想,即建構函式獲得資源,解構函式清理資源,但是當用一個智慧指標拷貝構造另一個智慧指標的時候,有可能會有淺拷貝的問題,這個空間會被釋放多次,智慧指標的發展就是圍繞著指標拷貝問題而走。

2.1 auto_ptr

(1)C++98裡面有一個智慧指標auto_ptr,對於拷貝構造和賦值運算子過載,該智慧指標採用管理權轉移的方式(當一個指標拷貝構造另一個指標時,當前指標就將對空間的管理權交給拷貝的那個指標,當前指標就指向空);
(2)但是這種方式不符合指標的要求(可以允許多個指標指向同一塊空間,將一個指標賦值給另一個指標的時候,就是需要讓兩個指標指向同一塊空間,而auto_ptr只允許一塊空間上只能有一個指標指向它),並且當管理權轉移之後要想再訪問之前的指標,就會出錯,因為之前的指標已經為NULL,就會出現解引用空指標的問題。

2.2 scoped_ptr/shared_ptr

因為auto_ptr有缺陷,但是C++標準裡面從C++98到C++11之間沒有出現新的智慧指標能解決這個缺陷,所以在這段時間內,boost這個官方組織就增加了智慧指標(scoped_ptr,shared_ptr,weak_ptr等)

(1)scoped_ptr採用防拷貝的方式(防拷貝就是不允許拷貝,拷貝就會出錯;防拷貝的實現:將拷貝構造和賦值運算子過載只宣告不實現,並且宣告為私有);
(2)shared_ptr為共享指標,裡面採用引用計數,當有shared_ptr指向同一塊空間的時候就增加引用計數,當引用計數減為0的時候才釋放該智慧指標管理的那塊空間。
(3)但是shared_ptr有一個缺點,就是會出現迴圈引用的問題(當一個shared_ptr(如sp1)管理的空間裡面包含一個shared_ptr的指標(_next),另一個shared_ptr(如sp2)管理的空間裡面也包含一個shared_ptr指標(_prev)時,當sp1->_next = sp2;sp2->_prev = sp1;此時就會使得sp1和sp2的引用計數都變為2,當出了這個作用域sp1和sp2的引用計數都會減為1,但是隻有引用計數為0時才會釋放管理的空間,就會使得sp1和sp2管理的空間沒有釋放。
(4)所以利用weak_ptr來解決迴圈引用的問題,weak_ptr叫弱指標,它主要是為了配合shared_ptr使用,用來解決迴圈引用的問題;

  • 將會出現迴圈引用問題的指標用weak_ptr儲存著,weak_ptr並不擁有這塊空間,所以weak_ptr裡面不增加shared_ptr的引用計數,也不會釋放這塊空間。(注意weak_ptr裡面也有自己的引用計數)

(5)boost庫裡面還包含scoped_array和shared_array(這個適用於delete[]的場景)

2.3 C++11(unique_ptr和shared_ptr)

(1)C++11借鑑了boost庫裡面的智慧指標(C++對應的智慧指標位於標頭檔案<memory>裡。

C++11裡面的unique_ptr就是boost庫裡面的scoped_ptr(防拷貝);
C++11裡面的shared_ptr就是boost裡面的shared_ptr。

(2)C++11裡面不包含類似於scoped_array和shared_array,而它採用定製刪除器的方式管理空間的釋放。

  • 定製刪除器就是自己指定採用何種方式釋放該空間(delete/free或其它);
  • 因為在實現智慧指標的過程中,我們需要管理資料的構造和析構,但不同的資料有不同的析構方式,就需要自己指定刪除方式。(例如new出來的資料,就必須用delete,而new[ ]就需要delete[ ]等)。注意:boost庫裡面也包含定製刪除器。

3 auto_ptr

1.原始碼分析:

   template<class _Ty>
   class auto_ptr
   {  
   public:
      typedef auto_ptr<_Ty> _Myt;
      typedef _Ty element_type;

        //建構函式
      explicit auto_ptr(_Ty *_Ptr = 0) _THROW0()
           : _Myptr(_Ptr)
           {}

        //拷貝建構函式,release返回儲存_Right指向空間的臨時變數,則_Myptr管理_Right管理的空間,並且將_Right置為空
      auto_ptr(_Myt& _Right) _THROW0()
           : _Myptr(_Right.release())
           {}

      //賦值運算子過載,_Right.release()將_Right的管理權轉移給_Myptr,將_Right置為空。reset裡面判斷是不是自己給自己賦值,不是就釋放_Myptr,並且讓_Myptr管理_Right以前管理的個指標,再返回*this。
      _Myt& operator=(_Myt& _Right) _THROW0()
           {  
           reset(_Right.release());
           return (*this);
           }

     //解構函式釋放_myptr
      ~auto_ptr() _NOEXCEPT
           {    
           delete _Myptr;
           }

      _Ty& operator*() const _THROW0()
           {  
 #if _ITERATOR_DEBUG_LEVEL == 2
           if (_Myptr == 0)
                 _DEBUG_ERROR("auto_ptr not dereferencable");
 #endif 

           return (*get());
           }

      _Ty *operator->() const _THROW0()
           {    
 #if _ITERATOR_DEBUG_LEVEL == 2
           if (_Myptr == 0)
                 _DEBUG_ERROR("auto_ptr not dereferencable");
 #endif
           return (get());
           }

       //get返回原生指標
      _Ty *get() const _THROW0()
           {     
           return (_Myptr);
           }

       //release用於管理權的轉移,將_myptr儲存在tmp裡面,將_mytmp的指標置為空,再返回tmp
      _Ty *release() _THROW0()
           {     // return wrapped pointer and give up ownership
           _Ty *_Tmp = _Myptr;
           _Myptr = 0;
           return (_Tmp);
           }

      void reset(_Ty *_Ptr = 0)
           {   
           if (_Ptr != _Myptr)
                 delete _Myptr;
           _Myptr = _Ptr;
           }

private:
      _Ty *_Myptr;     // the wrapped object pointer
      };

2.使用:(必須包含標頭檔案<memory>)
將動態開闢的指標交給一個智慧指標。

void Test_auto_ptr()
{
      auto_ptr<int> ap1(new int(10));
      cout << *ap1 << endl;  //輸出10

      auto_ptr<int> ap2(ap1);
      cout << *ap2 << endl;   //輸出10
      cout << *ap1 << endl;   //此時ap1為NULL,就是解引用空指標,程式崩潰
}

 scoped_ptr

1.原始碼:

template<class T> 
class scoped_ptr 
{
private:

    T * px;    //只含有一個成員變數T*的指標

     //將拷貝構造與賦值運算子過載宣告為私有,且不實現
    scoped_ptr(scoped_ptr const &);
    scoped_ptr & operator=(scoped_ptr const &);

    typedef scoped_ptr<T> this_type;

    void operator==( scoped_ptr const& ) const;
    void operator!=( scoped_ptr const& ) const;

public:

    typedef T element_type;

     //建構函式
    explicit scoped_ptr( T * p = 0 )
          : px( p ) 
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_constructor_hook( px );
#endif
    }

#ifndef BOOST_NO_AUTO_PTR

    explicit scoped_ptr( std::auto_ptr<T> p ) BOOST_NOEXCEPT 
         : px( p.release() )
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_constructor_hook( px );
#endif
    }

#endif

     //解構函式
    ~scoped_ptr() 
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_destructor_hook( px );
#endif
        boost::checked_delete( px );
    }

    void reset(T * p = 0) 
    {
        BOOST_ASSERT( p == 0 || p != px ); 
        this_type(p).swap(*this);
    }

    T & operator*() const 
    {
        BOOST_ASSERT( px != 0 );
        return *px;
    }

    T * operator->() const 
    {
        BOOST_ASSERT( px != 0 );
        return px;
    }

     //獲得原生指標
    T * get() const BOOST_NOEXCEPT
    {
        return px;
    }

#include <boost/smart_ptr/detail/operator_bool.hpp>

    void swap(scoped_ptr & b) BOOST_NOEXCEPT
    {
        T * tmp = b.px;
        b.px = px;
        px = tmp;
    }

2.使用:
當使用boost庫的時候,必須先要從boost官方網站下載boost的原始碼,然後將從boost庫下載的原始碼所在目錄包含至自己的專案,並且使用時要指定名稱空間為boost。

#include<iostream>
#include<string>
#include<boost/scoped_ptr.hpp>
#include<boost/scoped_array.hpp>

int main()
{
      boost::scoped_ptr<int> sp1(new int(10));

      //boost::scoped_ptr<int> sp2(sp1);     //不能拷貝

      boost::scoped_array<std::string> sp2(new std::string[10]);

      return 0;
}

5 boost庫shared_ptr的使用

void Test_boost_shared_ptr()
{
      boost::shared_ptr<int> sp1(new int(10));
      std::cout << *sp1 << std::endl;    //輸出10

      boost::shared_ptr<int> sp2(sp1);
      std::cout << *sp2 << std::endl;    //輸出10

      boost::shared_array<std::string> sp3(new std::string[10]);
      sp3[5] = "111";     //shared_array裡面過載了[],所以可以採用下標的方式進行讀寫

      boost::shared_array<std::string> sp4(sp3);
      std::cout << sp4[5] << std::endl;   //輸出111
}

6 C++11裡shared_ptr的使用

1.基本使用與boost裡shared_ptr的使用方式一致

void Test_Shared_ptr()
{
      std::shared_ptr<int> sp1(new int(20));
      std::shared_ptr<int> sp2(sp1);
      std::cout << *sp2 << std::endl;
}

2.由於C++11裡面沒有shared_array或scoped_array之類智慧指標,所以只能用shared_ptr需要自己定製刪除器:
(1)首先自己定製刪除器(例如我定製了一個delete和一個delete[ ])
自己需要編寫相應的仿函式,在用shared_ptr時不用將自己編寫的刪除器作為模板引數,但是在構造shared_ptr物件時需要傳相應仿函式的物件。

template<class T>
struct Delete
{
      void operator()(T* ptr)
      {
           delete ptr;
      }
};

template<class T>
struct DeleteArray
{
      void operator()(T* ptr)
      {
           delete[] ptr;
      }
};

(2)使用:

void Test_Shared_ptr()
{
      std::shared_ptr<int> sp1(new int(20));
      std::shared_ptr<int> sp2(sp1);
      std::cout << *sp2 << std::endl;

      DeleteArray<std::string> da;
      std::shared_ptr<std::string> sp3(new std::string[10],da); 

}

--------------------- 本文來自 Nicole xu 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/xu1105775448/article/details/80625936?utm_source=copy