tr1::shared_ptr 和 auto_ptr 都提供一個 get 成員函數進行顯示轉換,也就是說,返回一個智能指針對象內部的裸指針(raw pointer)(或它的一個副本):
int days = daysHeld(pInv.get()); // fine, passes the raw pointer // in pInv to daysHeld 就像實際上的所有智能指針類一樣,tr1::shared_ptr 和 auto_ptr 也都重載了指針解引用操作符(pointer dereferencing Operators)(operator-> 和 operator*),而這樣就答應隱式轉換到底層的裸指針(raw pointers):
class Investment { // root class for a hierarchy public: // of investment types bool isTaxFree() const; ... }; Investment* createInvestment(); // factory function
std::tr1::shared_ptr<Investment> // have tr1::shared_ptr pi1(createInvestment()); // manage a resource
bool taxable1 = !(pi1->isTaxFree()); // access resource // via operator-> ... std::auto_ptr<Investment> pi2(createInvestment()); // have auto_ptr // manage a // resource
bool taxable2 = !((*pi2).isTaxFree()); // access resource // via operator*
... 因為有些時候有必要取得 RAII 對象內部的裸資源,所以一些 RAII 類的設計者就通過提供一個隱式轉換函數來給剎車抹油。例如,考慮以下這個 RAII 類,它要為 C++ API 提供原始狀態的字體資源:
FontHandle getFont(); // from C API-params omitted // for simplicity
void releaseFont(FontHandle fh); // from the same C API class Font { // RAII class public: eXPlicit Font(FontHandle fh) // acquire resource; : f(fh) // use pass-by-value, because the {} // C API does
~Font() { releaseFont(f); } // release resource
PRivate: FontHandle f; // the raw font resource }; 假設有一個巨大的與字體有關的 C++ API 只能與 FontHandle 打交道,這就需要頻繁地將 Font 對象轉換為 FontHandle。Font 類可以提供一個顯式的轉換函數,比如 get:
class Font { public: ... FontHandle get() const { return f; } // explicit conversion function ... }; 不幸的是,這就要求客戶每次與 API 通信時都要調用 get:
void changeFontSize(FontHandle f, int newSize); // from the C API
Font f(getFont()); int newFontSize; ...
changeFontSize(f.get(), newFontSize); // explicitly convert // Font to FontHandle 一些程序員可能發現對顯式請求這個轉換的需求足以令人郁悶而避免使用這個類。反過來,設計 Font 類又是為了預防泄漏字體資源的機會的增長。
可選擇的辦法是為 Font 提供一個隱式轉換到它的 FontHandle 的轉換函數:
class Font { public: ... operator FontHandle() const { return f; } // implicit conversion function ... }; 這樣就可以使對 C API 的調用簡單而自然:
Font f(getFont()); int newFontSize; ...
changeFontSize(f, newFontSize); // implicitly convert Font // to FontHandle 不利的方面是隱式轉換增加了錯誤的機會。例如,一個客戶可能會在有意使用 Font 的地方意外地產生一個 FontHandle:
Font f1(getFont());
...
FontHandle f2 = f1; // oops! meant to copy a Font // object, but instead implicitly // converted f1 into its underlying // FontHandle, then copied that 現在,程序有了一個被 Font 對象 f1 治理的 FontHandle,但是這個 FontHandle 也能通過直接使用 f2 來加以利用。這幾乎絕對不會成為什么好事。例如,當 f1 被銷毀,字體將被釋放,f2 則被懸掛(dangle)。
關于是否提供從一個 RAII 類到它的底層資源的顯式轉換(例如,通過一個 get 成員函數)或者答應隱式轉換的決定,要依靠 RAII 類被設計履行的具體任務和它被計劃使用的細節而做出。最好的設計很可能就是堅持 Item 18 的建議(使接口易于正確使用,而難以錯誤使用)的那一個。通常,類似 get 的一個顯式轉換函數是更可取的方式,因為它將意外的類型轉換的機會減到最少。偶然的,通過隱式類型轉換提高使用的自然性將使天平向那個方向傾斜。