smart_ptr學習之scoped_ptr

 類如其名:scoped_ptr,僅僅只在自己的作用域內纔可以使用,出了自己的作用域就自動進行析構。

不同於auto_ptr,不支持所有權的轉移,同時把自己的拷貝構造函數、賦值操作符等相關的成員函數設定爲私有函數,更進一步的“鞏固”了“一切盡在我掌握中”的自信。

因爲不支持這些操作,所以對於所有權是完全不會轉移,這是與auto_ptr最大的不同點。

同時,因爲scoped_ptr在實現上僅僅是指對指針的單純proxy,因此在使用指針的地方基本上都可以使用它,並且沒有比“裸指針”付出更高的代價!

可以在如下幾種情況下使用該種智能指針:

(1)異常安全異常重要,因爲使用了RAII技術,即使出現異常安全,也能正確的進行資源的釋放

(2)動態分配的資源對象必須被嚴格的限定在一個作用域內,這也是該指針的特點

(3)正是因爲該指針的特點,不支持所有權的轉移(沒有實現的拷貝構造函數和賦值操作符),因此不支持作爲STL容器的元素

對於資源類型:支持以new操作符產生的相關資源,同時只支持delete操作符來釋放資源。

在利用該指針進行相關的代碼編寫過程中,要支持完整資源類型的分配和釋放<對應的就是非完整類型,只有聲明沒有定義的,一般在PIMPL中會碰到>,否則會報錯誤!

 

部分成員函數說明:

explicit scoped_ptr(T* p=0)

構造函數,存儲p的一份拷貝。注意,p 必須是用operator new分配的,或者是null. 在構造的時候,不要求T必須是一個完整的類型。當指針p是調用某個分配函數的結果而不是直接調用new得到的時候很有用:因爲這個類型不必是完整的,只需要類型T的一個前向聲明就可以了。這個構造函數不會拋出異常。

<前向聲明是一個比較有用的技術,我們在儘量減少文件的依賴性的時候,對於指針類型、引用類型、函數返回值類型都可以僅僅只需要一個前向聲明,不需要包含相關的聲明或者是定義的頭文件,那樣即使相關的頭文件發生了修改,那麼使用該聲明的頭文件也不用重新編譯>

explicit scoped_ptr( std::auto_ptr<T> p )

構造函數,獲得auot_ptr的所有權,屬於所有權的終止者。

~scoped_ptr()

刪除被指物。類型T在被銷燬時必須是一個完整的類型<會進行編譯時的錯誤檢查,判斷是否是完整類型>。如果scoped_ptr在它被析構時並沒有保存資源,它就什麼都不做。這個析構函數不會拋出異常。

void reset(T* p=0);

重置一個 scoped_ptr 就是刪除它已保存的指針,如果它有的話,並重新保存 p. 通常,資源的生存期管理應該完全由scoped_ptr自己處理,但是在極少數時候,資源需要在scoped_ptr的析構之前釋放,或者scoped_ptr要處理它原有資源之外的另外一個資源。這時,就可以用reset,但一定要儘量少用它。(過多地使用它通常表示有設計方面的問題) 這個函數不會拋出異常。

T& operator*() const;

返回一個到被保存指針指向的對象的引用。由於不允許空的引用,所以解引用一個擁有空指針的scoped_ptr將導致未定義行爲。如果不能肯定所含指針是否有效,就用函數get替代解引用。這個函數不會拋出異常。

T* operator->() const;

返回保存的指針。如果保存的指針爲空,則調用這個函數會導致未定義行爲。如果不能肯定指針是否空的,最好使用函數get。這個函數不會拋出異常。

T* get() const;

返回保存的指針。應該小心地使用get,因爲它可以直接操作裸指針。但是,get使得你可以測試保存的指針是否爲空。這個函數不會拋出異常。get通常用於調用那些需要裸指針的函數<一般是遺留的C程序代碼需要“裸指針”>,提醒智能指針的使用者:在使用智能指針的過程中,出了在智能指針的初始構造的時候需要直接使用指針資源,其它時候都儘量或者是不要直接使用裸指針,這個對於引用計數指針更是如此,需要謹記!

operator unspecified_bool_type() const

返回scoped_ptr是否爲非空。返回值的類型是未指明的,但這個類型可被用於Boolean的上下文中。在if語句中最好使用這個類型轉換函數,而不要用get去測試scoped_ptr的有效性<這個地方對外類型轉化可能大家不是很理解,爲什麼不直接重載operator bool()操作符,因爲bool操作符的重載會導致很多其它的類型轉化,比如說bool會轉換爲int等,那麼當該智能指針和int內置類型操作數一起的時候,就會產生一些未定義的行爲,關於這個操作符的重載,可以參考一些資料,一般都是使用自定義類中的某些函數指針等,因爲指針是可以轉換爲bool的(默認)>

void swap(scoped_ptr& b)

交換兩個scoped_ptr的內容。這個函數不會拋出異常。

 

全局swap函數,一般對於自定義類型可以提供該swap函數

template<typename T> void swap(scoped_ptr<T>& a,scoped_ptr<T>& b)

這個函數提供了交換兩個scoped pointer的內容的更好的方法。之所以說它更好,是因爲 swap(scoped1,scoped2) 可以更廣泛地用於很多指針類型,包括裸指針和第三方的智能指針。scoped1.swap(scoped2) 則只能用於它的定義所在的智能指針,而不能用於裸指針。

 

舉例:自己顯示的一下比較基礎的PIMPL

PScoped_ptr.hpp

#include <boost/scoped_ptr.hpp>

class PImp
{
private:
    struct SData;

    boost::scoped_ptr<SData> data;

public:
    PImp( int value );
    ~PImp();
    void set( int value );
    int  get( ) const;
};

PScoped_ptr.cpp

#include <iostream>
#include "PScoped_ptr.hpp"


struct PImp::SData
{
    SData( int v )
        :value(v)
    {}

    int value;
};


PImp::PImp( int value )
:data( new SData(value) )
{}

PImp::~PImp()
{}

void PImp::set( int value )
{
    *data = value;
}

int PImp::get() const
{
    return data->value;
}

PScoped_ptr_main.cpp

#include <iostream>
#include "PScoped_ptr.hpp"


int main()
{
    PImp imp(10);
    std::cout << imp.get() << std::endl;

    imp.set(11);
    std::cout << imp.get() << std::endl;
    return 0;
};

 

需要說明:注意在頭文件PScoped_ptr.hpp中,我們使用的SData是一個不完整的類型,只有聲明,還沒有定義;雖然是PImp的包含類型定義,但是沒有完整類型,如果不顯示的定義PImp的析構函數,那麼main主函數在編譯的過程中,會找不到相關的scoped_ptr在使用delete時候的析構函數,這個通過boost的庫的一個小工具checked_delete(T * x),在編譯的時候就會報錯,錯誤

1>d:\3rdlibs\include\boost\checked_delete.hpp(33) : error C2027: 使用了未定義類型“PImp::SData”
1>        f:\c++\boost\auto_ptrtest\auto_ptrtest\pscoped_ptr.hpp(6) : 參見“PImp::SData”的聲明
1>        d:\3rdlibs\include\boost\smart_ptr\scoped_ptr.hpp(81): 參見對正在編譯的函數 模板 實例化“void boost::checked_delete<T>(T *)”的引用
1>        with
1>        [
1>            T=PImp::SData
1>        ]
1>        d:\3rdlibs\include\boost\smart_ptr\scoped_ptr.hpp(77): 編譯類 模板 成員函數“boost::scoped_ptr<T>::~scoped_ptr(void)”時
1>        with
1>        [
1>            T=PImp::SData
1>        ]
1>        f:\c++\boost\auto_ptrtest\auto_ptrtest\pscoped_ptr.hpp(8): 參見對正在編譯的類 模板 實例化“boost::scoped_ptr<T>”的引用
1>        with
1>        [
1>            T=PImp::SData
1>        ]
1>d:\3rdlibs\include\boost\checked_delete.hpp(33) : error C2118: 負下標
1>d:\3rdlibs\include\boost\checked_delete.hpp(35) : warning C4150: 刪除指向不完整“PImp::SData”類型的指針;沒有調用析構函數
1>        f:\c++\boost\auto_ptrtest\auto_ptrtest\pscoped_ptr.hpp(6) : 參見“PImp::SData”的聲明

因此需要顯示定義相關的析構函數纔可以!

不過對於auto_ptr,則沒有這種要求,這也是我個人覺得auto_ptr不夠嚴格的地方,僅僅只會給出類似於warning C4150: 刪除指向不完整“PImp::SData”類型的指針;沒有調用析構函數警告,不夠嚴格。

輸出結果:

其它的使用,大家可以舉一反三!

在上面的實現中,是不支持拷貝和賦值操作符的,因爲成員變量(智能之爭)不支持,如果想實現相關的操作,需要自己手動的去實現相關的拷貝和賦值操作符。

 

scoped_ptr-----------資源泄露不用怕,一切盡在我掌握中,儘量不用“裸指針”,遠離資源泄露不是難事情。

當您想獲得和裸指針同樣的內存佈局大小,同樣的速度,但是有想異常安全,可以考慮使用它,不過你要自己費點心思寫copy construct和assign operator的操作了!

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章