博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++之RAII惯用法
阅读量:6436 次
发布时间:2019-06-23

本文共 2381 字,大约阅读时间需要 7 分钟。

hot3.png

标签: raiic++fpfileinitializationclass

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

举个常见的例子:

[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
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类并没有经过大量的实践,可能存在问题,请三思而慎用。这里只是记录下自己的实现想法。

转载于:https://my.oschina.net/innovation/blog/800617

你可能感兴趣的文章
web开发的跨域问题详解
查看>>
13.1-13.3 设置更改root密码,连接MySQL,MySQL常用命令
查看>>
【VMware vSAN 6.6】5.8.自动化:vSAN硬件服务器解决方案
查看>>
spring boot 配置文件
查看>>
小牛带你nginx反向代理中神奇的斜线
查看>>
Android Studio 2.1.2 升级到 2.2之后,gradle 编译版本更新为2.2.0,databinding报错
查看>>
今天来谈谈Python中的各种排序总结,含实现代码
查看>>
怎样将WPS转换word格式?如何进行操作
查看>>
针对不同创业阶段的创业者适合参加哪些创业赛事活动呢?创业是一场异常艰辛的马拉松,坚持是唯一的捷径,借...
查看>>
Linux中bash文档翻译
查看>>
解决win10不能安装NVIDIA的RTX 20系列的显卡驱动问题
查看>>
pdf如何解密
查看>>
jquery datatable的详细用法
查看>>
并发编程之 进程
查看>>
ansible 下lineinfile详细使用
查看>>
oracle 用函数返回对象集合
查看>>
猫猫学IOS(二十一)UIApplication设置程序图标右上⾓红⾊数字_联⺴指⽰器等
查看>>
Java(第十五章)
查看>>
Android--静默安装
查看>>
生命有尽,大道无涯
查看>>