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 的一个显式转换函数是更可取的方式,因为它将意外的类型转换的机会减到最少。偶然的,通过隐式类型转换提高使用的自然性将使天平向那个方向倾斜。