在之前的条款13,我们使用智能指针保存工厂函数如:createInvestment
的调用结果:
std::unique_ptr<Investment>p(createInvestment());
假如有一个函数来处理Investment
对象,像这样:
int daysHeld(const Investment* pi);
如果你想这样调用:
int days = daysHeld(p);
编译通不过,因为daysHeld
需要的是Investment*
指针,你传递的却是个类型为std::unique_ptr<Investment>
的对象。
这个时候就需要一个函数可以将RAII 类对象转换为原始资源。
有两种解决办法:显示转换和隐式转换。
智能指针提供了一个get()
方法,用来执行显示转换,它会返回智能指针内部的原始指针(的复件):
int days = daysHeld(p.get());
此外智能指针还重载了-> 和*操作符,它们允许隐式转换到底部原始指针。
由于有时候还是必须取得RAII对象内的原始资源。做法是提供一个隐式转换函数,比如下面这个类:
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,它们处理的是FontHandle
,那么将Font
对象转换为FontHandle
是一个很频繁的需求。
Font class可以提供一个显示转换函数,比如get
:
class Font
{
public:
...
FontHandle get()const{return f;}//显示转换函数
...
};
然后这样每次使用API都必须调用get.
另一个方法是令Font
提供隐式转换函数,转型为FontHandle
;
class Font
{
public:
...
operator FontHandle()const{return f;}
...
};
这样调用C API时就比较轻松自然,但是这个隐式转换会增加错误的机会,比如:
Font f1(getFont());
...
FontHandle f2 = f1;//f1隐式转换为FontHandle
原本是需要Font的,却转换成了FontHandle
,当f1被销毁,字体被释放,而f2将成为虚吊的.
你可以使用explicit
声明为显式的:
class Font
{
public:
...
explicit operator FontHandle()const{return f;}
...
};
当调用的时候,只有强制转换才会调用上面的转换函数。
但是这种方法和Get方法也差不多了。所以选择权取决于你。
总结:
1.API往往要求访问原始资源**,每一个RAII类应该提供一个取得其所管理资源的办法.**
2.对原始资源的访问有两种办法:显示转换和隐式转换。一般而言显式转换比较安全,隐式转换比较方便.