• C++之RAII惯用法


    http://blog.csdn.net/hunter8777/article/details/6327704

    C++中的RAII全称是“Resource acquisition is initialization”,直译为“资源获取就是初始化”。但是这翻译并没有显示出这个惯用法的真正内涵。RAII的好处在于它提供了一种资源自动管理的方式,当产生异常、回滚等现象时,RAII可以正确地释放掉资源。

    举个常见的例子:

    [cpp] view plain copy
    1. void Func()  
    2. {  
    3.   FILE *fp;  
    4.   char* filename = "test.txt";  
    5.   if((fp=fopen(filename,"r"))==NULL)  
    6.   {  
    7.       printf("not open");  
    8.       exit(0);  
    9.   }  
    10.   ... // 如果 在使用fp指针时产生异常 并退出  
    11.        // 那么 fp文件就没有正常关闭  
    12.       
    13.   fclose(fp);  
    14. }  

    在资源的获取到释放之间,我们往往需要使用资源,但常常一些不可预计的异常是在使用过程中产生,就会使资源的释放环节没有得到执行。

    此时,就可以让RAII惯用法大显身手了。

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

    具体示例代码如下:

    [cpp] view plain copy
    1. class Resource{};  
    2. class RAII{  
    3. public:  
    4.     RAII(Resource* aResource):r_(aResource){} //获取资源  
    5.     ~RAII() {delete r_;} //释放资源  
    6.     Resource* get()    {return r_ ;} //访问资源  
    7. private:  
    8.     Resource* r_;  
    9. };  

    比如文件操作的例子,我们的RAII临时对象类就可以写成:

    [cpp] view plain copy
    1. class FileRAII{  
    2. public:  
    3.     FileRAII(FILE* aFile):file_(aFile){}  
    4.     ~FileRAII() { fclose(file_); }//在析构函数中进行文件关闭  
    5.     FILE* get() {return file_;}  
    6. private:  
    7.     FILE* file_;  
    8. };  

    则上面这个打开文件的例子就可以用RAII改写为:

    [cpp] view plain copy
    1. void Func()  
    2. {  
    3.   FILE *fp;  
    4.   char* filename = "test.txt";  
    5.   if((fp=fopen(filename,"r"))==NULL)  
    6.   {  
    7.       printf("not open");  
    8.       exit(0);  
    9.   }  
    10.   FileRAII fileRAII(fp);  
    11.   ... // 如果 在使用fp指针时产生异常 并退出  
    12.        // 那么 fileRAII在栈展开过程中会被自动释放,析构函数也就会自动地将fp关闭  
    13.     
    14.   // 即使所有代码是都正确执行了,也无需手动释放fp,fileRAII它的生命期在此结束时,它的析构函数会自动执行!      
    15.  }  

    这就是RAII的魅力,它免除了对需要谨慎使用资源时而产生的大量维护代码。在保证资源正确处理的情况下,还使得代码的可读性也提高了不少。

    创建自己的RAII类

    一般情况下,RAII临时对象不允许复制和赋值,当然更不允许在heap上创建,所以先写下一个RAII的base类,使子类私有继承Base类来禁用这些操作:

    [cpp] view plain copy
    1. class RAIIBase  
    2. {  
    3. public:  
    4.     RAIIBase(){}  
    5.     ~RAIIBase(){}//由于不能使用该类的指针,定义虚函数是完全没有必要的  
    6.       
    7.     RAIIBase (const RAIIBase &);  
    8.     RAIIBase & operator = (const RAIIBase &);  
    9.     void * operator new(size_t size);   
    10.     // 不定义任何成员  
    11. };  

    当我们要写自己的RAII类时就可以直接继承该类的实现:

    [cpp] view plain copy
    1. template<typename T>  
    2. class ResourceHandle: private RAIIBase //私有继承 禁用Base的所有继承操作  
    3. {  
    4. public:  
    5.     explicit ResourceHandle(T * aResource):r_(aResource){}//获取资源  
    6.     ~ResourceHandle() {delete r_;} //释放资源  
    7.     T *get()    {return r_ ;} //访问资源  
    8. private:  
    9.     T * r_;  
    10. };  

    将Handle类做成模板类,这样就可以将class类型放入其中。另外, ResourceHandle可以根据不同资源类型的释放形式来定义不同的析构函数。

    由于不能使用该类的指针,所以使用虚函数是没有意义的。

    注:自己写的RAII类并没有经过大量的实践,可能存在问题,请三思而慎用。这里只是记录下自己的实现想法。

  • 相关阅读:
    SkinSharp用法
    nosql和关系型数据库比较?
    Java实现 蓝桥杯VIP 算法提高 进制转换
    Java实现 蓝桥杯VIP 算法提高 3-2字符串输入输出函数
    Java实现 蓝桥杯VIP 算法提高 3-2字符串输入输出函数
    Java实现 蓝桥杯VIP 算法提高 3-2字符串输入输出函数
    Java实现 蓝桥杯VIP 算法提高 3-2字符串输入输出函数
    Java实现 蓝桥杯VIP 算法提高 3-2字符串输入输出函数
    Java实现 蓝桥杯VIP 算法提高 去注释
    Java实现 蓝桥杯VIP 算法提高 去注释
  • 原文地址:https://www.cnblogs.com/feng9exe/p/7246747.html
Copyright © 2020-2023  润新知