最近开始看《Effective C++》,为了方便以后回顾,特意做了笔记。若本人对书中的知识点理解有误的话,望请指正!!!
在资源管理类中存储了我们的原始资源,保证了我们的原始资源必定会执行某些操作,但是有时我们需要用到我们的原始资源,所以资源管理类需要提供对原始资源的访问。如条款13中的投资的例子,我们现在需增加一个获得投资天数的接口
int daysHeld(const Investment* pi); //返回某个Investment对象的投资天数 shared_ptr<Investment> pInv(createInvestment()); //某个Investment的智能指针对象 int days = daysHeld(pInv); //把智能指针作为参数传入,这是错误的
编译器会在第4行报错,因为传入参数类型不匹配,daysHeld()
需要的是 Investment*
类型,所以这时我们需要从智能指针中访问到原始资源 Investment*
。有两个方法可以达到这个目的:显示转换和隐式转换。
shared_ptr
访问原始资源:
get()
返回智能指针内的原始指针(的复件)。-> 、*
)允许隐式转换至底部原始指针int days = daysHeld(pInv.get());//显示转换 class Investment{ public: boos isTaxFree() const; ... }; //隐式转换 bool taxable1 = !(pInv->isTaxFree()); //使用->操作符 bool taxable2 = !((*pInv).isTaxFree()); //使用*操作符
因此,我们自定义的资源管理类有时也需要提供一个访问原始资源的途径。如C API提供的字体 FontHandle
分配和释放,我们定义一个可以访问原始资源的资源管理类 Font
。
FontHandle getFont(); //C API定义的分配字体函数 void releaseFont(FontHandle fh); //C API定义的释放字体函数 class Font{ public: explicit Font(FontHandle fh):f(fh) { //C只能使用值传递 //构造时获取资源 } ~Font() { releaseFont(f);//析构时释放资源 } //显式转换函数 FontHandle get() const { return f; } //隐式转换函数 operator FontHandle() const { return f; } ... private: FontHandle f; }; void changeFontSize(FontHandle f, int newSize); //改变字体大小的C API int main() { Font f(getFont()); int newFontSize; ... changeFontSize(f.get(), newFontSize); //需要使用get()来显式转换 changeFontSize(f, newFontSize); //隐式转换 }
缺点:
显示转换
get()
函数隐式转换
会增加错误发生的机会
Font f1(getFont()); ... //原本是想拷贝一个Font对象f2,但不小心把Font写成FontHandle了 //这会将f1隐式转换为其底部的FontHandle,然后对它拷贝一份副本返回 //编译器不会对此发出报错的信息,但是当f1被销毁,字体被释放,就会导致f2的字体被损坏。 FontHandle f2 = f1;
到底使用显示转换还是印是转换,取决于自己设计的资源管理类的需求。通常显式转换的 get()
函数可能是更好的选择,但如果想要代码自然易懂,隐式转换可能更好,两者各有优缺点。
Note:
- API 往往要求访问原始资源,所以每一个RAII class 应该提供一个”取得其管理的资源“的方法
- 对原始资源的访问可能经由显示转换或隐式转换。一般而言显示转换比较安全,但隐式转换对用户比较方便
条款16:成对使用new和delete时采取相同形式