条款15:在资源管理类中提供原始资源的访问

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34938530/article/details/89108355

  当你使用智能如auto_ptr或std::tr1::share_ptr保存一个factory函数如createInvestment的调用结果时:

std::tr1::shared_ptr<Investment> pInv(createInvestment());

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

int daysHeld(const Investment* pi);            //返回投资天数

  你想要这样调用它:

  

int days = daysHeld(pInv);            //错误

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

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

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

  

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

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

class Investment{                    //investment继承体系的根类
public:
    bool isTaxFree() const;
    ...
};

Investment* createInvesment();                             //factory函数
std::tr1::shared_ptr<Investment>  pil(createInvestment);   //令tr1::shared_ptr管理一笔资源

bool taxable1 =! (pil->isTaxFree());                       //经由operator->访问资源
...
std::auto_ptr<Investment> pi2(createInvestment());         //令auto_ptr管理一笔资源

bool taxable2 = !((*pi2).isTaxFree());                     //经由operator* 访问资源

由于有时候还是必须取得RAII对象内的原始资源,某些RAII class设计者于是联想到“将油脂涂在滑轨上”,做法是提供一个隐式装换函数。

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,它们处理的是FontHandles,那么“将Font对象装换成FontHandle”会是一种很频繁的需求。Font class可为此提供一个显示转换函数,像get那样:

class Font{
public:
    ...
    FontHandle get() const {return f;}    //显示转换函数
};

但是每次使用API时就必须调用get:

void changeFontSize(FontHandle f,int newSize);    //C API
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 f1(getFont());
...
FontHandle f2 = f1;             //原本是拷贝一个Font对象,却反而将f1隐式转换为底部的FontHandle                            
                                //然后才复制它

 以上程序有个FontHandle由Font对象f1管理,但那个FontHandle也可以通过直接使用f2取得。假如当f1被销毁,字体被释放,而f2因此成为“虚吊的”(dangle)。

  结论:

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

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

猜你喜欢

转载自blog.csdn.net/qq_34938530/article/details/89108355