條款15:在資源管理類張提供對原始資源的訪問

請牢記:安全

一、APIs每每要求訪問原始資源,因此每個RAII class應該提供一個「取得其所管理之資源」的辦法。數據結構

二、對原始資源的訪問可能經由顯式轉換或隱式轉換。通常而言顯式轉換比較安全,但隱式轉換對客戶比較方便。函數

 

前面條款13的例子中使用了智能指針如auto_ptr或tr1::shared_ptr保存createInvestment的調用結果:字體

std::tr1::shared_ptr<Investment> pInv(createInvestment()); 

假如某個API但願以某個函數處理Investment對象,返回投資天數,以下:spa

int dayHeld(const Investment* pi);  //返回投資天數

調用時:指針

int days = dayHeld(pInv);  //error!

由於dayHeld須要的是Investment*指針,而非std::shared_ptr<Investment>的對象!!對象

這時就須要一個函數可將RAII類對象轉換爲所含原始資源(本例爲Investment*),有兩個方法:顯示轉換和隱式轉換。blog

顯示轉換:繼承

tr1::shared_ptr和auto_ptr都提供get成員函數,用來執行顯示轉換,返回智能指針內部的原始指針。
ci

int days = dayHeld(pInv.get());   //將pInv的原始指針傳遞給dayHeld

隱式轉換:

tr1::shared_ptr和auto_ptr也重載了指針取值操做符(operator->和operator*),容許隱式轉換至底部原始指針。

class Investment{                           //investment繼承體系的根類
public:
    bool isTaxFree() const;
    ...
};
Investment* createInvestment();                  //factory函數
std::tr1::shared_ptr<Investment> pi1(createInvestment());  //令tr1::shared_ptr管理一筆資源
bool taxable1 = !(pi1->isTaxFree());               //經由operator->訪問資源
...
std::auto_ptr<Investment> pi2(createInvestment());      //令auto_ptr管理一筆資源
bool taxable2 = !((*pi2).isTaxFree());             //經由operator*訪問資源
...

因爲有時候仍是必須取得RAII對象內的原始資源,作法是提供一個隱式轉換函數,考慮下面的用於字體的RAII類(對CAPI而言字體是一種原生數據結構):

FontHandle getFont();   //這是一個C API

  

void releaseFont(FontHandle fh);   //一樣是C API

  

class Font
{
public:
	explicit Font(FontHandle fh)  //得到資源。 explicit只對構造函數起做用,用來抑制隱式轉換。
		:f(fh)	        //採用值傳遞 
	{
	}
	~Font()
	{
		releaseFont(f);  //釋放資源
        } 
private:
	FontHandle f;
};

若是有大量的FontHandles 那麼將Font轉換成FontHandle比較頻繁。Font 類可爲此提供一個顯示轉換函數:

class Font{
public:
    ...
    FontHandle get() const {return f;} //顯示轉換函數
    ...
};

不幸的是客戶每次使用API都必須調用get:

void changeFontSize(FontHandle f,int newSize); //C API
Font f(getFont());
int newFontSize;
...
changeFontSize(f.get(),newFontSize);  //明白地將Font轉換爲FontHandle

另外一個方法是提供隱式轉換函數

class Font{
public:
    ...
    operator FontHandle() const //隱式轉換函數  //轉換操做符 定義成 operator T()
    {return f;}
    ...
};

調用:

Font f(getFont());
int newFontSize;
...
changeFontSize(f,newFontSize);   //將Font隱式轉換爲FontHandle

可是這個隱式轉換會增長錯誤發生機會。例如客戶可能會在須要Font時意外建立一個FontHandle:

Font f1(getFont());
...
FontHandle f2 = f1;   //原意是想copy一個Font對象,結果確將f1隱式轉換爲FontHandle而後copy它。

使用顯式仍是隱式,取決於具體的使用狀況。通常而言顯式轉換比較安全,但隱式轉換對客戶比較方便

相關文章
相關標籤/搜索