当你使用智能如auto_ptr或std::tr1::share_ptr保存一个factory函数如createInvestment的调用结果时:
std::tr1::shared_ptr<Investment> pInv(createInvestment());
假设你希望以某个函数处理Investment对象,像这样:
int daysHeld(const Investment* pi); //返回投资天数
你想要这样调用它:
int days = daysHeld(pInv); //错误
编译不会通过,因为daysHeld需要的是Investment* 指针,你传递给它的却是个类型为tr1::shared_ptr<Investment>的对象。
这时候你需要一个函数可将RAII对象转换成其所内含之原始资源。有两个做法可以达成目标:显示装换和隐式装换。
tr1::shared_ptr和auto_ptr都提供一个get成员函数,用来执行显示装换,也就是它会返回智能指针内部的原始指针(的复件)。
int days = daysHeld(pInv.get());//将pInv内的原始指针传给daysHeld
就像(几乎)所有智能指针一样,tr1::shared_ptr和auto_ptr也重载了指针取值(pointer dereferencing)操作符(operatot->和operator*),它们允许隐式装换至底部原始指针:
class Investment{ //investment继承体系的根类
public:
bool isTaxFree() const;
...
};
Investment* createInvesment(); //factory函数
std::tr1::shared_ptr<Investment> pil(createInvestment); //令tr1::shared_ptr管理一笔资源
bool taxable1 =! (pil->isTaxFree()); //经由operator->访问资源
...
std::auto_ptr<Investment> pi2(createInvestment()); //令auto_ptr管理一笔资源
bool taxable2 = !((*pi2).isTaxFree()); //经由operator* 访问资源
由于有时候还是必须取得RAII对象内的原始资源,某些RAII class设计者于是联想到“将油脂涂在滑轨上”,做法是提供一个隐式装换函数。
FontHandle getFont(); //这是个C API。为求简化暂略参数
void releaseFont(FontHandle fh); //来自同一组C API
class Font{ //RAII class
public:
explicit Font(FontHandle fh) //获得资源;
:f(fh)
{ }
~Font(){releaseFont(f);} //释放资源
private:
FontHandle f; //原始字体资源
};
假设有大量与字体相关的C API,它们处理的是FontHandles,那么“将Font对象装换成FontHandle”会是一种很频繁的需求。Font class可为此提供一个显示转换函数,像get那样:
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
另一个办法是令Font提供隐式装换函数,转型为FontHandle:
class Font{
public:
...
operator FontHandle() const //隐式装换函数
{return f;}
...
};
这种方式会是调用显示很自然:
Font f(getFont());
int newFontSize;
...
changeFontSize(f, newFontSize); //将Font隐式转换成FontHandle
但是这个隐式转换会增加错误发生机会:
Font f1(getFont());
...
FontHandle f2 = f1; //原本是拷贝一个Font对象,却反而将f1隐式转换为底部的FontHandle
//然后才复制它
以上程序有个FontHandle由Font对象f1管理,但那个FontHandle也可以通过直接使用f2取得。假如当f1被销毁,字体被释放,而f2因此成为“虚吊的”(dangle)。
结论:
APIs往往要求访问原始资源,所以每一个RAII class应该提供一个“取得一个其所管理之资源”的办法。
对原始资源的访问可能经由显示转换或隐式转换。一般而言显示转换比较安全,但隐式转换对调用者比较方便。