effective C++ 条款 15:在资源管理类中提供对原始资源的访问
资源管理类避免直接处理资源,但是许多APIs直接涉及资源,所以应该提供返回原始资源的函数。
tr1::shared_ptr和auto_ptr都提供一个get成员函数,用来执行显式转换,返回智能指针内部的原始指针(的复件)。
std::tr1::shared_ptr<Investment> pInv(createInvestment());
int daysHeld(const Investment* pi); //返回投资的天数
int days = daysHeld(pInv); //错误;需要的是Investment而传的是std::tr1::shared_ptr<Investment>对象。
int days = daysHeld(pInv.get()) //将pInv中的原始指针传给daysHeld
几乎所有智能指针都重载了指针取值操作符(operator->和operator*)允许隐式转换至底部原始指针:
class Investment {
public:
bool isTaxFree()const;
};
Investment* createInvestment();
void f()
{
std::tr1::shared_ptr<Investment> pInv1(createInvestment());
bool taxable = !(pInv1->isTaxFree());//经由operator->访问资源
...
std::tr1::shared_ptr<Investment> pInv2(pInv1);
bool taxable2 = !((*pInv2).isTaxFree());//经由operator*访问资源
...
}
FontHandle getFont(); //C API。为简化,暂略参数
void releaseFont(FontHandle fh);
class Font
{
public:
explicit Font(FontHandle fh)
: f(fh)
{
}
FontHandle get()const{return f;} //显式转换函数
{
return f;
}
~Font(){releaseFont(f);}
protected:
private:
FontHandle f;
};
void changeFontSize(FontHandle f, int newSize);
Font f(getFont());
int newFontSize;
...
changeFontSize(f.get(), newFontSize);
某些程序员认为这样到处处理显式转换,让人倒尽胃口,另一个办法是令Font提供隐式转换函数,转型为FontHandle:
class Font
{
public:
explicit Font(FontHandle fh)
: f(fh)
{
}
operator FontHandle() const //隐式转换函数
{
return f;
}
~Font(){releaseFont(f);}
protected:
private:
FontHandle f;
};
这样客户调用c api时比较轻松自然:
void changeFontSize(FontHandle f, int newSize);
Font f(getFont());
int newFontSize;
...
changeFontSize(f/*.get()*/, newFontSize); //将Font隐式转换为FontHandle
但是,这个隐式转换会增加错误发生机会,例如客户会在需要Font时意外创建一个FontHandle:
Font f1(getFont());
…
FontHandle f2 = f1; //本意是拷贝一个Font对象管理资源。
//却将f1隐式转换成其底部的FontHandle然后复制它。
以上FontHandle由Font对象f1管理, 但那个FontHandle也可通过直接使用f2取得。那几乎不会有好下场,例如当f1被销毁,字体被释放,而f2因此而成为“虚吊的”(dangle)。
RAII class内返回原始资源的函数,确实与“封装”发生矛盾,但RAII class 并不是为了封装某物而存在;他们存在是为了确保一个特殊行为--资源释放--会发生。