歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> C++智能指針相關知識點

C++智能指針相關知識點

日期:2017/3/1 9:08:34   编辑:Linux編程

智能指針與異常

如果使用智能指針, 如果程序塊過早的結束, 智能指針能保證在內存不再需要時進行釋放. (特別是在有多個出口的函數中 -- 雖然應盡量避免設計這樣的函數, 但凡事總有例外 -- 此時使用智能指針來自動釋放內存是非常方便的)
對於異常處理來說, 考慮下面兩個函數, 當程序發生異常時, 智能指針也能正確的釋放內存.
  void f3()
  {
    int* p = new int(10);
    throw "some error infomations";
    delete p;
  }

void f4()
{
    std::shared_ptr<int> sp = std::make_shared<int>(10);
    throw "some error infomations";
}

使用智能指針時的注意事項

C++智能指針使用時需要注意的事項, C++11中已經廢棄了 auto_ptr, 因此不再討論其用法, 無特殊說明, 下面的事項對 auto_ptr 而言, 可能是不正確的.

(1) 不要把一個原生指針給多個智能指針對象管理, 對所有的智能指針對象都成立

    int* p = new int(2);
    std::shared_ptr<int> sp0(p);
    std::shared_ptr<int> sp1(p); // 錯誤, 不能將同一原始指針對象給兩個智能指針對象管理

(2) 不要把 this 指針給智能指針對象, 對所有的智能指針對象(包括 auto_ptr)都成立, 下面的代碼演示錯誤的使用方法  

        #define PRINT_FUN() printf("%s:%d\n", __FUNCTION__, __LINE__)
        
        class CTest{
        public:
            CTest(){};
            ~CTest(){
                PRINT_FUN();
            };
            void Run()
            {
                m_sp = std::shared_ptr<CTest>(this); // 錯誤, 當 CTest 對象釋放時也會釋放 m_sp , 此時會再次 delete CTest 對象. (析構函數中的打印消息可以看出會出現一個對象兩次調用析構函數.)
            }
        private:
            std::shared_ptr<CTest> m_sp;
        };
        
        std::shared_ptr<CTest> sp(new CTest());
        sp->Run();
        或者這樣寫
        CTest t;
        t.Run();    

  (3) 不要在函數實參裡創建智能指針對象

function ( shared_ptr<int>(new int), g( ) ); //有缺陷
可能的過程是先 new int, 然後調 g( ), g( )發生異常, shared_ptr<int> 沒有創建, int內存洩露
推薦寫法:
shared_ptr<int> p(new int());
f(p, g());

(4) 處理不是 new 創建的對象要小心. 如果確實需要這樣做, 需要智能指針傳遞一個刪除器, 自定義刪除行為.

    int* pi = (int*) malloc(4);
    shared_ptr<int> sp(pi); // shared_ptr 析構時將調用 delete. 使用 malloc 分配內存, 用 delete 釋放顯然不對.

  (5) 不要使用 new 創建一個智能指針對象.如 new shared_ptr<T> : 本來 shared_ptr 就是為了管理指針資源的, 不要又引入一個需要管理的指針資源shared_ptr<T>*

  (6) 使用 dynamic_pointer_cast 進行轉換(C++11 中已廢棄 shared_dynamic_cast)

            class B
            {
            public:
                B(){};
                virtual ~B(){};
            };
            class D : public B
            {
            public:
                D(){};
                virtual ~D(){};
            };

            std::shared_ptr<B> sp(new D);
            B* b = sp.get();
            D* d = dynamic_cast<D*>(b);
        正確用法:
            std::shared_ptr<B> spb(new D);
            std::shared_ptr<D> spd = std::dynamic_pointer_cast<D>(spb);

  (7) 不要 memcpy 智能指針對象

shared_ptr<B> sp1(new B);
shared_ptr<B> sp2;
memcpy(&sp2, &sp1, sizeof(shared_ptr<B>)); //sp2.use_count()==1
很顯然,不是通過正常途徑(拷貝構造,賦值運算),引用計數是不會正確增長的。

(8) 智能指針對象數組的使用, 需要自定義釋放器.

    shared_ptr 數組, std::shared_ptr<A> p(new A[10], std::default_delete<A[]>());
    std::unique_ptr<int[]>(new int[10], std::default_delete<int[]>());

  (9) 將智能指針對象作為函數參數傳遞時要小心, 如下面的代碼, 當調用所在的表達式結束(即函數調用返回)時, 這個臨時對象就被銷毀了, 它所指向的內存也被釋放.

      int* pa = new int(10); // 小心, 不是一個智能指針
      f(std::shared_ptr<int>(pa)); // 合法的, 但內存會被釋放
      int a = *pa; // 錯誤, pa已經被釋放, 但繼續指向已經釋放的內存, 從而變成了一個空懸指針, 現在試圖訪問 pa 的值, 其結果是未定義的
    應該這樣使用:
      std::shared_ptr<int> sp(new int(10));
      f(std::shared_ptr<int>(sp)); // 調用拷貝構造函數, sp.use_count == 2

  (10) 當將一個智能指針對象(如 shared_ptr)綁定到一個普通指針時, 就將內存管理的責任交給了這個 shared_ptr. 此後就不應該使用內置指針來訪問 shared_ptr 所指向的內存了.

  (11) 不能使用 delete 釋放 get 返回的普通指針. get 函數的設計是為了向不能使用智能指針的代碼傳遞一個普通指針, 應該減少 get 函數的調用.

  (12) 不要使用 get 返回的普通指針來初始化另一個智能指針, 或為另一個智能指針賦值. 顯然如果這樣做, 將導致兩次釋放相同的內存, 或者其中一個已經將內存釋放, 但另一個還在使用.

std::shared_ptr<int> sp = std::make_shared<int>(10);
int* p = sp.get();
{
  std::shared_ptr<int> sp(p);
}
int x = *sp;

Copyright © Linux教程網 All Rights Reserved