标签: raiic++fpfileinitializationclassc++
C++中的RAII全称是“Resource acquisition is initialization”,直译为“资源获取就是初始化”。可是这翻译并无显示出这个惯用法的真正内涵。RAII的好处在于它提供了一种资源自动管理的方式,当产生异常、回滚等现象时,RAII能够正确地释放掉资源。函数
举个常见的例子:ui
[cpp] view plain copy翻译
void Func() { FILE *fp; char* filename = "test.txt"; if((fp=fopen(filename,"r"))==NULL) { printf("not open"); exit(0); } ... // 若是 在使用fp指针时产生异常 并退出 // 那么 fp文件就没有正常关闭 fclose(fp); }
在资源的获取到释放之间,咱们每每须要使用资源,但经常一些不可预计的异常是在使用过程当中产生,就会使资源的释放环节没有获得执行。指针
此时,就可让RAII惯用法大显身手了。code
RAII的实现原理很简单,利用stack上的临时对象生命期是程序自动管理的这一特色,将咱们的资源释放操做封装在一个临时对象中。对象
具体示例代码以下:继承
[cpp] view plain copyci
class Resource{}; class RAII{ public: RAII(Resource* aResource):r_(aResource){} //获取资源 ~RAII() {delete r_;} //释放资源 Resource* get() {return r_ ;} //访问资源 private: Resource* r_; };
好比文件操做的例子,咱们的RAII临时对象类就能够写成:资源
[cpp] view plain copy
class FileRAII{ public: FileRAII(FILE* aFile):file_(aFile){} ~FileRAII() { fclose(file_); }//在析构函数中进行文件关闭 FILE* get() {return file_;} private: FILE* file_; };
则上面这个打开文件的例子就能够用RAII改写为:
[cpp] view plain copy
void Func() { FILE *fp; char* filename = "test.txt"; if((fp=fopen(filename,"r"))==NULL) { printf("not open"); exit(0); } FileRAII fileRAII(fp); ... // 若是 在使用fp指针时产生异常 并退出 // 那么 fileRAII在栈展开过程当中会被自动释放,析构函数也就会自动地将fp关闭 // 即便全部代码是都正确执行了,也无需手动释放fp,fileRAII它的生命期在此结束时,它的析构函数会自动执行! }
这就是RAII的魅力,它免除了对须要谨慎使用资源时而产生的大量维护代码。在保证资源正确处理的状况下,还使得代码的可读性也提升了很多。
建立本身的RAII类
通常状况下,RAII临时对象不容许复制和赋值,固然更不容许在heap上建立,因此先写下一个RAII的base类,使子类私有继承Base类来禁用这些操做:
[cpp] view plain copy
class RAIIBase { public: RAIIBase(){} ~RAIIBase(){}//因为不能使用该类的指针,定义虚函数是彻底没有必要的 RAIIBase (const RAIIBase &); RAIIBase & operator = (const RAIIBase &); void * operator new(size_t size); // 不定义任何成员 };
当咱们要写本身的RAII类时就能够直接继承该类的实现:
[cpp] view plain copy
template<typename T> class ResourceHandle: private RAIIBase //私有继承 禁用Base的全部继承操做 { public: explicit ResourceHandle(T * aResource):r_(aResource){}//获取资源 ~ResourceHandle() {delete r_;} //释放资源 T *get() {return r_ ;} //访问资源 private: T * r_; };
将Handle类作成模板类,这样就能够将class类型放入其中。另外, ResourceHandle能够根据不一样资源类型的释放形式来定义不一样的析构函数。
因为不能使用该类的指针,因此使用虚函数是没有意义的。
注:本身写的RAII类并无通过大量的实践,可能存在问题,请三思而慎用。这里只是记录下本身的实现想法。