许多API函数直接指涉资源,除非你永远不用这样的API函数,否则只得绕过资源管理对象直接访问原始资源。
例:条款13导入一个观念:使用智能指针如auto_ptr或tr1::shared_ptr保持factory函数如createInvestment的调用结果:
- std::tr1::shared_ptr<Investment> pInv(createInvestment());
假如你希望某个函数处理Investment对象,像这样:
- int dayHelp(const Investment* pi);//返回投资天数
你想要这么调用它:
- int days = dayHelp(pInv);//error!!
却通不过编译,因为dayHelp需要的是Investment*指针,你传给它的却是个类型为tr1::shared_ptr<Investment>的对象。
这时候你需要一个函数将RAII类对象转换为其所内含之原始资源。有两个做法可以达成目的:显式转换和隐式转换。
tr1::shared_ptr和auto_ptr都提供了一个get成员函数,用来显示转换,也就是它会返回智能指针内部的原始指针(的副本)。
- int days = dayHelp(pInv.get());//将pInv内的原始指针传给dayHelp
就像所有智能指针一样auto_ptr和tr1::shared_ptr也重载了指针取值操作符(operator->和operator*),它们允许隐式转换至底部原始指针:
- class Investment //investment继承体系的跟类
- {
- public:
- bool isTaxFree() const;
- };
- Investment* createInvestment();//工厂函数
- std::tr1::shared_ptr<Investment> pil(createInvestment());
- //令tr1::shared_ptr管理一笔资源
- bool taxable1 = !(pil->isTaxFree());//经由operator->访问资源
- ...
- std::tr1::shared_ptr<Investment> pi2(createInvestment());
- bool taxable2 = !((*pi2).isTaxFree());//经由operator*访问资源
- ...
由于有时候还是必须取得RAII对象内的原始资源,做法是提供一个隐式转换函数。
考虑下面这个字体的RAII类
- FontHandle getFont();//这是个C API。为求简化省略参数
- void releaseFont(FontHandle fh);//来自同一组的C API
- class Font
- {
- public:
- explicit Font(FontHandle fh) ; f(fh)//获得资源
- {
- }
- ~Font()
- {
- releaseFont(f);//释放资源
- }
- private:
- FontHandle f; //原始字体资源
- };
假设有大量与字体相关的C API,它们处理的是FontHandle,那么“将Font对象转换为FontHandle”会是一种很频繁的需求,Font类可为此提供一个显示转换函数,像get那样:
- class Font
- {
- public:
- ...
- FontHandle get() const
- {
- return f;//显示转换函数
- }
- };
不幸的是客户每当使用API函数必须调用get
- void changeFontSize(FontHandle f, int newSize);
- 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时意外创建一个FontHandle:
- Font f1(getFont());
- ...
- FontHandle f2 = f1;//拷贝了一个Font对象,
- //却反而将f1隐式转换为其底部的FontHandle
- //然后才复制它
以上程序有个FontHandle有Font对象f1管理,但是那个FontHandle也可以通过直接使用f2取得,这样很容易出问题。例如当f1被销毁,字体被释放,而f2因此策划观念为一个“虚吊的”。
是否该提供一个显示转换函数将RAII类转换为其底部资源,还是应该提供隐式转换函数,答案主要取决于RAII类被设计执行的特定工作,以及被使用的情况。最近设计很可能坚持条款18:“让接口容易被正确使用,不易被误用”。
API往往要求访问原始资源,所以每一个RAII类应该提供一个“取得其所管理的资源”的办法。
对原始资源的访问可能经由显示转换或隐式转换,一般而言显示转换比较安全,但隐式转换对客户比较方便。