0.概述
- APIs往往要求访问原始资源(raw resources),所以每一个RAII class应该提供一个“取得其所管理之资源”的办法。
- 对原始资源的访问可能经由显式转换或隐式转换。一般而言显式转换比较安全,但隐式转换对客户比较方便。
1.举例
资源管理类是对抗资源泄露的堡垒。但是很多API会需要直接访问原始资源,这就需要绕过资源管理对象。
std::tr1::shared_ptr<Investment> pInv(createInvestment()); //使用智能指针保存工厂函数的调用结果
希望某个函数处理Investment对象:
int daysHeld(const Investment* pi); //返回投资天数
调用:
int days=daysHeld(pInv);
上述代码无法通过编译,因为daysHeld需要Investment*指针,而传递的是tr1::shared_ptr<Investment>对象。
2.将RAII class对象转换为其内含的原始资源
2.1 显示转换
trl::shared_ ptr和auto_ptr都提供一个get成员函数,用来执行显式转换,也就是它会返回智能指针内部的原始指针(的复件):
int days = daysHeld(pInv.get());
2.2 隐式转换
作为智能指针,trl::shared_ ptr和auto_ptr都重载了指针取值操作符operator->和operator*,允许隐式转换至底部原始指针:
class Investment
{
public:
bool isTaxFree() const;
...
};
Investment* createInvestment();
std::tr1::shared_ptr<Investment> pi1(createInvestment());
bool taxable1 = !(pi1->isTaxFree());
std::auto_ptr<Investment> pi2(createInvestment());
bool taxable2 = !((*pi2).isTaxFree());
3.取得RAII对象内的原始资源
3.1 RAII类提供显式转换函数
用于字体的RAII类:
FontHandle getFont();
void releaseFont(FontHandle fh);
class Font{ //RAII class
public:
explicit Font(FontHandle fh):f(fh){} //获得资源
~Font(){releaseFont(f);} //释放资源
private:
FontHandle f; //原始字体资源
};
如果有大量API处理的是FontHandles,将Font对象转换为FontHandle操作将非常频繁。Font类可以提供一个显式转换函数,类似于get:
FontHandle get() const {return f;}
但这样的话每次使用API时都必须调用get:
void changeFontSize(FontHandle f, int newSize);
changeFontSize(f.get(), newFontSize);//显式转换函数
3.2 RAII类提供隐式转换函数
operator FontHandle() const{return f;}
调用:
changeFontSize(f, newFontSize);//隐式转换
隐式转换比较轻松自然,但是会增加错误发生机会,客户可能会在需要Font时意外创建FontHandle:
Font f1(getFont());
...
FontHandle f2=f1; //本意是要拷贝Font对象,但反而将f1隐式转换为其底部的FontHandle,然后才拷贝
4.补充说明
- 一般而言显式转换比较安全,但隐式转换对客户比较方便。使用时需要权衡。
- RAII中返回原始资源的函数确实与封装发生矛盾,但RAII并不是为了封装某物而存在,而是为了确保资源释放这一行为会发生。