請牢記:安全
一、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它。
使用顯式仍是隱式,取決於具體的使用狀況。通常而言顯式轉換比較安全,但隱式轉換對客戶比較方便。