条款15:在资源管理类中提供对原始资源的访问
资源管理类是对抗资源泄露的有效手段。在完美的系统中,我们将依赖这样的类来处理和资源之间的所有互动,而不是直接手动处理原始资源。然而许多APIs直接指涉资源。举个例子,条款13中,使用智能指针如auto_ptr或shared_ptr保存factory函数如createInvestment的调用结果:
shared_ptr<Investment>pInv(createInvestment());
假设你希望以某个函数处理Investment对象,像这样:
int daysHeld(const Investment* pi); //返回投资天数
你想要这么调用它:
int days = daysHeld(pInv); //错误
上述调用编译不通过,因为daysHeld需要的是Investment*指针,你传给它的却是类型为shared_ptr<Investment.>的对象。
这时候我们需要一个函数可将RAII class对象转换为其所内含的原始资源(本例为Investment*)。有两个办法可以做到:显示转换和隐式转换。
void FontHandle getFont(); //C API
void changeFontSize(FontHandle f, int newSize);//C API
void releaseFont(FontHandle fh); //C API
class Font {
public:
explicit Font(FontHandle fh):f(fh) {}
~Font() {releaseFont(f);}
FontHande get() const {return f;} //显示转换函数
private:
FontHandle f; //底层原始资源
};
不幸的是这使得客户每当想要使用API时就必须调用get:
Font f(getFont());
int newSize;
changeFontSize(f.get(), newSize); //显示调用转换函数
另一个办法是令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,
//然后才复制它。
请记住
- APIs往往要求访问原始资源,所以每一个RAII class应该提供一个转换函数。
- 对原始资源的访问可能经由显示转换或者隐式转换。一般而言显示转换比较安全1,但隐式转换对客户比较方便。