许多API函数直接指涉资源,除非你永远不用这样的API函数,否则只得绕过资源管理对象直接访问原始资源。

例:条款13导入一个观念:使用智能指针如auto_ptr或tr1::shared_ptr保持factory函数如createInvestment的调用结果:

   
   
   
   
  1. std::tr1::shared_ptr pInv(createInvestment()); 

假如你希望某个函数处理Investment对象,像这样:

   
   
   
   
  1. int dayHelp(const Investment* pi);//返回投资天数 

你想要这么调用它:

   
   
   
   
  1. int days = dayHelp(pInv);//error!! 

却通不过编译,因为dayHelp需要的是Investment*指针,你传给它的却是个类型为tr1::shared_ptr的对象。

这时候你需要一个函数将RAII类对象转换为其所内含之原始资源。有两个做法可以达成目的:显式转换和隐式转换。

tr1::shared_ptr和auto_ptr都提供了一个get成员函数,用来显示转换,也就是它会返回智能指针内部的原始指针(的副本)。

   
   
   
   
  1. int days = dayHelp(pInv.get());//将pInv内的原始指针传给dayHelp 

就像所有智能指针一样auto_ptr和tr1::shared_ptr也重载了指针取值操作符(operator->和operator*),它们允许隐式转换至底部原始指针:

   
   
   
   
  1. class Investment //investment继承体系的跟类 
  2. public
  3.   bool isTaxFree() const
  4. }; 
  5. Investment* createInvestment();//工厂函数 
  6. std::tr1::shared_ptr pil(createInvestment()); 
  7.         //令tr1::shared_ptr管理一笔资源 
  8.  
  9. bool taxable1 = !(pil->isTaxFree());//经由operator->访问资源 
  10. ... 
  11. std::tr1::shared_ptr pi2(createInvestment()); 
  12. bool taxable2 = !((*pi2).isTaxFree());//经由operator*访问资源 
  13. ... 

由于有时候还是必须取得RAII对象内的原始资源,做法是提供一个隐式转换函数。

考虑下面这个字体的RAII类

   
   
   
   
  1. FontHandle getFont();//这是个C API。为求简化省略参数 
  2. void releaseFont(FontHandle fh);//来自同一组的C API 
  3. class Font 
  4. public
  5.   explicit Font(FontHandle fh) ;  f(fh)//获得资源 
  6.   { 
  7.   } 
  8.   ~Font() 
  9.   { 
  10.     releaseFont(f);//释放资源 
  11.   } 
  12. private
  13.   FontHandle f; //原始字体资源 
  14. }; 

假设有大量与字体相关的C API,它们处理的是FontHandle,那么“将Font对象转换为FontHandle”会是一种很频繁的需求,Font类可为此提供一个显示转换函数,像get那样:

   
   
   
   
  1. class Font 
  2. public
  3.   ... 
  4.   FontHandle get() const 
  5.   { 
  6.     return f;//显示转换函数 
  7.   } 
  8. }; 

不幸的是客户每当使用API函数必须调用get

   
   
   
   
  1. void changeFontSize(FontHandle f, int newSize); 
  2. Font f(getFont()); 
  3. int newFontSize; 
  4. ... 
  5. changeFontSize(f.get(),newFontSize);//明白地将Font转换为FontHandle 

 

另一个办法是令Font提供隐式转换函数,转换为FontHandle:

   
   
   
   
  1. class Font 
  2. public
  3.   ... 
  4.   operator FontHandle() const //隐式转换函数 
  5.   { 
  6.     return f; 
  7.   } 
  8.   ... 
  9. }; 

   
   
   
   
  1. Font f(getFont()); 
  2. int newFontSize; 
  3. ... 
  4. changeFontSize(f, newFontSize);//将Font隐式转换为FontHandle 

但是这个隐式转换会增加错误机会,例如客户可能会在需要Font时意外创建一个FontHandle:

   
   
   
   
  1. Font f1(getFont()); 
  2. ... 
  3. FontHandle f2 = f1;//拷贝了一个Font对象, 
  4.                    //却反而将f1隐式转换为其底部的FontHandle 
  5.                    //然后才复制它 

以上程序有个FontHandle有Font对象f1管理,但是那个FontHandle也可以通过直接使用f2取得,这样很容易出问题。例如当f1被销毁,字体被释放,而f2因此策划观念为一个“虚吊的”。

 

是否该提供一个显示转换函数将RAII类转换为其底部资源,还是应该提供隐式转换函数,答案主要取决于RAII类被设计执行的特定工作,以及被使用的情况。最近设计很可能坚持条款18:“让接口容易被正确使用,不易被误用”。

 

API往往要求访问原始资源,所以每一个RAII类应该提供一个“取得其所管理的资源”的办法。

对原始资源的访问可能经由显示转换或隐式转换,一般而言显示转换比较安全,但隐式转换对客户比较方便。