摘要

RAII技术被认为是C++中管理资源的最佳方法,进一步引申,使用RAII技术也可以实现安全、简洁的状态管理,编写出优雅的异常安全的代码。

资源管理

RAII是C++的发明者Bjarne Stroustrup提出的概念,RAII全称是“Resource Acquisition is Initialization”,直译过来是“资源获取即初始化”,也就是说在构造函数中申请分配资源,在析构函数中释放资源。因为C++的语言机制保证了,当一个对象创建的时候,自动调用构造函数,当对象超出作用域的时候会自动调用析构函数。所以,在RAII的指导下,我们应该使用类来管理资源,将资源和对象的生命周期绑定。

智能指针(std::shared_ptr和std::unique_ptr)即RAII最具代表的实现,使用智能指针,可以实现自动的内存管理,再也不需要担心忘记delete造成的内存泄漏。毫不夸张的来讲,有了智能指针,代码中几乎不需要再出现delete了。

内存只是资源的一种,在这里我们讨论一下更加广义的资源管理。比如说文件的打开与关闭、windows中句柄的获取与释放等等。按照常规的RAII技术需要写一堆管理它们的类,有的时候显得比较麻烦。但是如果手动释放,通常还要考虑各种异常处理,比如说:

  1. void function()
  2. {
  3. FILE *f = fopen("test.txt", 'r');
  4. if (.....)
  5. {
  6. fclose(f);
  7. return;
  8. }
  9. else if(.....)
  10. {
  11. fclose(f);
  12. return;
  13. }
  14.  
  15. fclose(f);
  16. ......
  17. }

这里介绍一个网上实现的我认为比较简洁的方法,文章在这里。作者使用了C++11标准中的lambda表达式和std::function相结合的方法,非常简洁、明了。直接看代码吧:

  1. #define SCOPEGUARD_LINENAME_CAT(name, line) name##line
  2. #define SCOPEGUARD_LINENAME(name, line) SCOPEGUARD_LINENAME_CAT(name, line)
  3. #define ON_SCOPE_EXIT(callback) ScopeGuard SCOPEGUARD_LINENAME(EXIT, __LINE__)(callback)
  4.  
  5. class ScopeGuard
  6. {
  7. public:
  8. explicit ScopeGuard(std::function<void()> f) :
  9. handle_exit_scope_(f){};
  10.  
  11. ~ScopeGuard(){ handle_exit_scope_(); }
  12. private:
  13. std::function<void()> handle_exit_scope_;
  14. };
  15.  
  16. int main()
  17. {
  18. {
  19. A *a = new A();
  20. ON_SCOPE_EXIT([&] {delete a; });
  21. ......
  22. }
  23.  
  24. {
  25. std::ofstream f("test.txt");
  26. ON_SCOPE_EXIT([&] {f.close(); });
  27. ......
  28. }
  29.  
  30. system("pause");
  31. return 0;
  32. }

作者为了使用方便,还定义了根据行号来对ScopeGuard类型对象命名的宏定义。看到了吧,当ScopeGuard对象超出作用域,ScopeGuard的析构函数中会调用handle_exit_scope_函数,也就是lambda表达式中的内容,所以在lamabda表达式中填上资源释放的代码即可,多么简洁、明了。既不需要为每种资源管理单独写对应的管理类,也不需要考虑手动释放出现各种异常情况下的处理,同时资源的申请和释放放在一起去写,永远不会忘记。

状态管理

RAII另一个引申的应用是可以实现安全的状态管理。一个典型的应用就是在线程同步中,使用std::unique_lock或者std::lock_guard对互斥量std:: mutex进行状态管理。通常我们不会写出如下的代码:

  1. std::mutex mutex_;
  2. void function()
  3. {
  4. mutex_.lock();
  5. ......
  6. ......
  7. mutex_.unlock();
  8. }

因为,在互斥量lock和unlock之间的代码很可能会出现异常,或者有return语句,这样的话,互斥量就不会正确的unlock,会导致线程的死锁。所以正确的方式是使用std::unique_lock或者std::lock_guard对互斥量进行状态管理:

  1. std::mutex mutex_;
  2. void function()
  3. {
  4. std::lock_guard<std::mutex> lock(mutex_);
  5. ......
  6. ......
  7. }

在创建std::lock_guard对象的时候,会对std::mutex对象进行lock,当std::lock_guard对象在超出作用域时,会自动std::mutex对象进行解锁,这样的话,就不用担心代码异常造成的线程死锁。

总结

通过上面的分析可以看出,RAII的核心思想是将资源或者状态与对象的生命周期绑定,通过C++的语言机制,实现资源和状态的安全管理。理解和使用RAII能使软件设计更清晰,代码更健壮。

参考

1、http://mindhacks.cn/2012/08/27/modern-cpp-practices/

2、http://www.jellythink.com/archives/101

3、http://www.cppblog.com/aaxron/archive/2011/03/22/142475.html

C++中的RAII介绍 资源管理的更多相关文章

  1. C++中的RAII介绍

    摘要 RAII技术被认为是C++中管理资源的最佳方法,进一步引申,使用RAII技术也可以实现安全.简洁的状态管理,编写出优雅的异常安全的代码. 资源管理 RAII是C++的发明者Bjarne Stro ...

  2. AutoMapper之ABP项目中的使用介绍

    最近在研究ABP项目,昨天写了Castle Windsor常用介绍以及其在ABP项目的应用介绍 欢迎各位拍砖,有关ABP的介绍请看阳光铭睿 博客 AutoMapper只要用来数据转换,在园里已经有很多 ...

  3. iOS开发UI篇—iPad开发中得modal介绍

    iOS开发UI篇—iPad开发中得modal介绍 一.简单介绍 说明1: 在iPhone开发中,Modal是一种常见的切换控制器的方式 默认是从屏幕底部往上弹出,直到完全盖住后面的内容为止 说明2: ...

  4. objective-c 中的关联介绍

    objective-c 中的关联介绍 转载请注明CSDN博客上的出处: http://blog.csdn.net/daiyibo123/article/details/46471993 如何设置关联 ...

  5. ORACLE 中的 锁 介绍

    ORACLE 中的 锁 介绍 Oracle数据库支持多个用户同时与数据库进行交互,每个用户都可以同时运行自己的事务,从而也需要对并发访问进行控制.Oracle也是用“锁”的机制来防止各个事务之间的相互 ...

  6. Android中Snackbar的介绍以及使用

    Android中Snackbar的介绍以及使用 介绍 Snackbar可以说是Toast的升级版,不仅有显示信息的功能,还可以添加一个Action,实现点击功能,可以右滑删除. 效果图 Snackba ...

  7. 03_MyBatis基本查询,mapper文件的定义,测试代码的编写,resultMap配置返回值,sql片段配置,select标签标签中的内容介绍,配置使用二级缓存,使用别名的数据类型,条件查询ma

     1 PersonTestMapper.xml中的内容如下: <?xmlversion="1.0"encoding="UTF-8"?> < ...

  8. [翻译]Mock 在 Python 中的使用介绍

    目录 Mock 在 Python 中的使用介绍 原文链接与说明 恐惧系统调用 一个简单的删除函数 使用 Mock 重构 潜在陷阱 向 'rm' 中加入验证 将文件删除作为服务 方法 1:模拟实例的方法 ...

  9. netcore中的缓存介绍

    Cache(缓存)是优化web应用的常用方法,缓存存放在服务端的内存中,被所有用户共享.由于Cache存放在服务器的内存中,所以用户获取缓存资源的速度远比从服务器硬盘中获取快,但是从资源占有的角度考虑 ...

随机推荐

  1. mybatis由浅入深day02_8spring和mybatis整合

    8 spring和mybatis整合 8.1 整合思路 需要spring通过单例方式管理SqlSessionFactory.mapper接口. spring和mybatis整合生成代理对象,使用Sql ...

  2. python 协程(单线程中的异步调用)(转廖雪峰老师python教程)

    协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在 ...

  3. ios 调用系统应用的方法 应用间跳转的方法

    声明一个私有方法: #pragma mark - 私有方法 -(void)openUrl:(NSString *)urlStr{ //注意url中包含协议名称,iOS根据协议确定调用哪个应用,例如发送 ...

  4. Access数据操作-02

    数据库连接 MDB文件 :Provider=Microsoft.Jet.OLEDB.4.0;Data Source=*.mdb ;Persist Security Info=False; AccDB文 ...

  5. js 对象引用传值

    1)当变量是一个对象(例如:{}或[]),使用 a = b = {} 这种形式传值的话,会变成会用传值,修改 a 时 b 会相应变化,修改 b 时 a 也一样会相应变化 var a = b = {}; ...

  6. java -jar Test.jar找不到main class问题

    如果你写了一些class,编 译到一个jar里面,但是在执行这个jar的时候报错,找不到main class,但是你明明是有的,是不是很郁闷,检查一下你的jar吧. 解压你的jar,打开META-IN ...

  7. echo\awk\sed\tee\curl的使用-shell

    echo的使用:http://man.linuxde.net/echo awk的使用:http://man.linuxde.net/awk sed的使用:http://man.linuxde.net/ ...

  8. 关于HTML5的十大面试题

    1.html5的文档类型和字符集分别是什么? 答:<!doctype html>/<meta charset="UTF-8"> 2.在html5中如何嵌入一 ...

  9. java基础---->多线程之Runnable(一)

    java线程的创建有两种方式,这里我们通过简单的实例来学习一下.一切都明明白白,但我们仍匆匆错过,因为你相信命运,因为我怀疑生活. java中多线程的创建 一.通过继承Thread类来创建多线程 pu ...

  10. CSS- ie6,ie7,ie8 兼容性写法,CSS hack写法

    css ie6,ie7,ie8 兼容性写法,CSS hack写法 margin-bottom:40px;       /*ff的属性*/margin-bottom:140px\9;    /* IE6 ...