Boost智能指標-基礎篇

來源:互聯網
上載者:User

標籤:c++   boost   智能指標   

簡介

記憶體管理一直是 C++ 一個比較繁瑣的問題,而智能指標卻可以很好的解決這個問題,在初始化時就已經預定了刪除,排解了後顧之憂。1998年修訂的第一版C++標準只提供了一種智能指標:std::auto_ptr(現以廢棄),它基本上就像是個普通的指標:通過地址來訪問一個動態分配的對象。std::auto_ptr之所以被看作是智能指標,是因為它會在析構的時候調用delete操作符來自動釋放所包含的對象。當然這要求在初始化的時候,傳給它一個由new操作符返回的對象的地址。既然std::auto_ptr的解構函式會調用delete操作符,它所包含的對象的記憶體會確保釋放掉。這是智能指標的一個優點。
當嘗試和異常聯絡起來時這就更加重要了:沒有std::auto_ptr這樣的智能指標,每一個動態分配記憶體的函數都需要捕捉所有可能的異常,以確保在異常傳遞給函數的調用者之前將記憶體釋放掉。Boost C++ 庫的智能指標系列提供了許多可以用在各種場合的智能指標。


RAII

RAII全稱是“Resource acquisition is initialization”,直譯為“資源擷取就是初始化”。但是這翻譯並沒有顯示出這個慣用法的真正內涵。RAII的好處在於它提供了一種資源自動管理的方式,當產生異常、復原等現象時,RAII可以正確地釋放掉資源。 
RAII也是智能指標的基本原理,智能指標只是這個習語的其中一例。智能指標確保在任何情況下,動態分配的記憶體都能得到正確釋放,從而將開發人員從這項任務中解放了出來。 這包括程式因為異常而中斷,原本用於釋放記憶體的代碼被跳過的情境。用一個動態分配的對象的地址來初始化智能指標,在析構的時候釋放記憶體,就確保了這一點。因為解構函式總是會被執行的,這樣所包含的記憶體也將總是會被釋放。
許多的 C++ 應用程式都需要動態管理記憶體,因而智能指標是一種很重要的 RAII 類型,不過 RAII 本身是適用於許多其它情境的。


scoped_ptr

boost::scoped_ptr是一個簡單的智能指標,它能夠保證在離開範圍後對象被自動釋放。
boost::scoped_ptr的實現是利用了一個棧上的對象去管理一個堆上的對象,從而使得堆上的對象隨著棧上的對象銷毀時自動刪除。
boost::scoped_ptr 特點

  • 不能轉換所有權
    scoped_ptr所管理的對象生命週期僅僅局限於一個區間(該指標所在的"{}"之間),無法傳到區間之外,這就意味著scoped_ptr對象是不能作為函數的返回值的。
  • 不能共用所有權
    這個特點一方面使得該指標簡單易用。另一方面也造成了功能的薄弱:不能用於STL的容器中。
  • 不能用於管理數組對象
    由於scoped_ptr是通過delete來刪除所管理對象的,而數組對象必須通過deletep[]來刪除,因此scoped_ptr是不能管理數組對象的,如果要管理數組對象需要使用boost::scoped_array類。

shared_ptr

boost::scoped_ptr雖然簡單易用,但它不能共用所有權的特性卻大大限制了其使用範圍,shared_ptr可以解決這一局限。在標準C++中為 std::shared_ptr ,在Boost C++庫裡,這個智能指標命名為boost::shared_ptr。
shared_ptr的管理機制其實並不複雜,就是對所管理的對象進行了引用計數,當新增一個shared_ptr對該對象進行管理時,就將該對象的引用計數加一;減少一個shared_ptr對該對象進行管理時,就將該對象的引用計數減一,如果該對象的引用計數為0的時候,說明沒有任何指標對其管理,才調用delete釋放其所佔的記憶體。

boost::shared_ptr 特點

  • 可以共用對象的所有權
    和scoped_ptr相比shared_ptr可以共用對象的所有權,所以儲存在容器中的拷貝(包括容器在需要時額外建立的拷貝)都是和原件相同的,可以在標準容器中安全的使用動態分配的對象。
  • 安全執行緒的
    shared_ptr 對象提供與內建類型一樣的安全執行緒層級。一個 shared_ptr 執行個體可以同時被多個線程“讀”(僅使用不變操作進行訪問)。 不同的 shared_ptr 執行個體可以同時被多個線程“寫入”(使用類似 operator= 或 reset 這樣的可變操作進行訪問)(即使這些執行個體是拷貝,而且共用下層的引用計數)。 任何其它的同時訪問的結果會導致未定義行為。”
    • 同一個shared_ptr被多個線程“讀”是安全的。
    • 同一個shared_ptr被多個線程“寫”是不安全的。
    • 共用引用計數的不同的shared_ptr被多個線程”寫“是安全的。
  • 支援自訂的deleter

boost::shared_ptr 注意事項

  • shared_ptr可以當做函數的參數,也可以當做函數的返回值,這時候相當於使用複製構造
  • shared_ptr可以被用於標準容器,複製時相當於使用複製構造
  • 要注意不要循環參考,那樣會造成對象不會被釋放

weak_ptr

shared_ptr引用計數是一種便利的記憶體管理機制,但它有一個很大的缺點,那就是不能管理循環參考的對象。例如:

#include <string>#include <iostream>#include <boost/shared_ptr.hpp>#include <boost/weak_ptr.hpp>class parent;class children;typedef boost::shared_ptr<parent> parent_ptr;typedef boost::shared_ptr<children> children_ptr;class parent{public:    ~parent() { std::cout <<"destroying parent\n"; }public:    children_ptr children;};class children{public:    ~children() { std::cout <<"destroying children\n"; }public:    parent_ptr parent;};void test(){    parent_ptr father(new parent());    children_ptr son(new children);    father->children = son;    son->parent = father;}void main(){    std::cout<<"begin test...\n";    test();    std::cout<<"end test.\n";}

運行該程式可以看到,即使退出了test函數後,由於parent和children對象互相引用,它們的引用計數都是1,不能自動釋放,並且此時這兩個對象再無法訪問到,這就引起了記憶體流失。
一般來講,解除這種循環參考有下面有三種可行的方法:

  • 當只剩下最後一個引用的時候需要手動打破循環參考釋放對象。
  • 當parent的生存期超過children的生存期的時候,children改為使用一個普通指標指向parent。
  • 使用弱引用的智能指標打破這種循環參考。在父對子引用時使用強引用,子對父引用時使用弱引用,從而避免了循環參考。

雖然這三種方法都可行,但方法1和方法2都需要程式員手動控制,麻煩且容易出錯。這裡主要介紹一下第三種方法和boost中的弱引用的智能指標boost::weak_ptr。

強引用
一個強引用當被引用的對象活著的話,這個引用也存在(就是說,當至少有一個強引用,那麼這個對象就不能被釋放)。boost::share_ptr就是強引用。

弱引用
它僅僅是對象存在時候的引用,當對象不存在時弱引用能夠檢測到,從而避免非法訪問,弱引用也不會修改對象的引用計數。這意味這弱引用它並不對對象的記憶體進行管理,在功能上類似於普通指標,然而一個比較大的區別是,弱引用能檢測到所管理的對象是否已經被釋放,從而避免訪問非法記憶體。

在C++中,普通指標可看做弱引用,智能指標可看做強引用,儘管指標不能算"真正"的弱引用,因為弱引用應該能知道何時對象變成不可訪問的了。

打破循環參考
在父對子引用時使用強引用,子對父引用時使用弱引用,從而避免了循環參考。

//詳細例子見 Boost智能指標-weak_ptrclass children{public:    ~children() { std::cout <<"destroying children\n"; }public:    boost::weak_ptr<parent> parent;};

雖然通過弱引用指標可以有效解除循環參考,但這種方式必須在程式員能預見會出現循環參考的情況下才能使用,也可以是說這個僅僅是一種編譯期的解決方案,如果程式在運行過程中出現了循環參考,還是會造成記憶體流失的。


intrusive_ptr

在多數情況下,我們不使用 boost::intrusive_ptr, 因為共用所有權的功能已在 boost::shared_ptr中提供,而且非插入式智能指標比插入式智能指標更靈活。
但是,有時候也會需要插入式的引用計數,可能是由於舊的代碼,或者是為了與第三方的類進行整合。當有這種需要時,可以用 intrusive_ptr ,它具有與其它Boost智能指標相同的語義。如果你使用過其它的Boost智能指標,你就會發現不論是否插入式的,所有智能指標都有一致的介面。
使用intrusive_ptr的類必須可以提供引用計數。intrusive_ptr 通過調用兩個函數,intrusive_ptr_add_ref 和 intrusive_ptr_release來管理引用計數;這兩個函數必須正確地操作插入式的引用計數,以保證 intrusive_ptr正確工作。在使用intrusive_ptr的類中已經內建有引用計數的情況下,實現對intrusive_ptr的支援就是實現這兩個函數。有些情況下,可以建立這兩個函數的參數化版本,然後對所有帶插入式引用計數的類型使用相同的實現。多數時候,聲明這兩個函數的最好的地方就是它們所支援的類型所在的名字空間。 在以下情況時使用 intrusive_ptr

  • 需要把 this 當作智能指標來使用。
  • 已有代碼使用或提供了插入式的引用計數。
  • 智能指標的大小必須與裸指標的大小相等。

對比boost::shared_ptr

使用boost::shared_ptr使用者類本省不需要具有引用計數功能,而是由boost::shared_ptr來提供;使用boost::shared_ptr的一大陷阱就是用一個raw pointer多次建立boost::shared_ptr,這將導致該raw pointer被多次銷毀當boost::shared_ptr析構時。即不能如下使用:

int *a = new int(5);boost::shared_ptr ptr1(a);boost::shared_ptr ptr2(a);  //錯誤! 

boost::intrusive_ptr完全具備boost::shared_ptr的功能,且不存在shared_ptr的問題,即可以利用raw pointer建立多個intrusive _ptr,其原因就在於引用計數的ref_count對象,shared_ptr是放在shared_ptr結構裡,而目標對象T通過繼承intrusive_ptr_base將引用計數作為T對象的內部成員變數,就不會出現同一個對象有兩個引用計數器的情況出現。

那麼為什麼通常鼓勵大家使用shared_ptr,而不是intrusive_ptr呢, 在於shared_ptr不是侵入性的,可以指向任意類型的對象; 而intrusive_ptr所要指向的對象,需要繼承intrusive_ptr_base,即使不需要,引用計數成員也會被建立。


參考連結
http://zh.wikipedia.org/wiki/%E5%BC%B1%E5%BC%95%E7%94%A8 http://www.codeproject.com/Articles/8394/Smart-Pointers-to-boost-your-code http://www.cnblogs.com/TianFang/archive/2008/09/15/1291050.html http://www.cnblogs.com/TianFang/archive/2008/09/19/1294521.html http://www.cnblogs.com/TianFang/archive/2008/09/20/1294590.html http://blog.csdn.net/alai04/article/details/572959 http://blog.csdn.net/yockie/article/details/8840205 http://blog.csdn.net/juana1/article/details/6624222 http://blog.csdn.net/ithzhang/article/details/9038929 http://blog.csdn.net/hunter8777/article/details/6327704 http://blog.csdn.net/yusiguyuan/article/details/22037833


From:http://blog.csdn.net/liufei_learning/article/details/34808549

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.