通过RAII机制实现资源的自动释放

利用RALL机制使用模板技术蒋某中类型的指针封装在RALL类的临时对象中避免指针在被使用的过程中发生异常,通过私有继承避免RALL的被复制和拷贝
举个常见的例子:

[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惯用法大显身手了。

 

RAII的实现原理很简单,利用stack上的临时对象生命期是程序自动管理的这一特点,将我们的资源释放操作封装在一个临时对象中。

具体示例代码如下:

[cpp] view plain copy
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可以根据不同资源类型的释放形式来定义不同的析构函数。

你可能感兴趣的:(技术,内存,对象,指针)