摘要

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技术需要写一堆管理它们的类,有的时候显得比较麻烦。但是如果手动释放,通常还要考虑各种异常处理,比如说:

void function()
{
FILE *f = fopen("test.txt", 'r');
if (.....)
{
fclose(f);
return;
}
else if(.....)
{
fclose(f);
return;
} fclose(f);
......
}

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

#define SCOPEGUARD_LINENAME_CAT(name, line) name##line
#define SCOPEGUARD_LINENAME(name, line) SCOPEGUARD_LINENAME_CAT(name, line)
#define ON_SCOPE_EXIT(callback) ScopeGuard SCOPEGUARD_LINENAME(EXIT, __LINE__)(callback) class ScopeGuard
{
public:
explicit ScopeGuard(std::function<void()> f) :
handle_exit_scope_(f){}; ~ScopeGuard(){ handle_exit_scope_(); }
private:
std::function<void()> handle_exit_scope_;
}; int main()
{
{
A *a = new A();
ON_SCOPE_EXIT([&] {delete a; });
......
} {
std::ofstream f("test.txt");
ON_SCOPE_EXIT([&] {f.close(); });
......
} system("pause");
return 0;
}

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

状态管理

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

std::mutex mutex_;
void function()
{
mutex_.lock();
......
......
mutex_.unlock();
}

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

std::mutex mutex_;
void function()
{
std::lock_guard<std::mutex> lock(mutex_);
......
......
}

在创建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. Oracle-随机数获取

    1.获取10-100的数据,保留两位小数 select trunc(dbms_random.value(10,100),2) from dual ; 2.获取0-1的小数 select dbms_ra ...

  2. MongoDB(一)-- 简介、安装、CRUD

    一.Mongodb简介 MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统. 在高负载的情况下,添加更多的节点,可以保证服务器性能. MongoDB 旨在为WEB应用提供可 ...

  3. 如何在单片机上使用printf函数(printf)(avr)(stm)(lpc)(单片机)(转)

    摘要:    当我们在调试代码时,通常需要将程序中的某个变量打印至PC机上,来判断我们的程序是否按预期的运行,printf函数很好的做到了这一点,它能直接以字符的方式输出变量名和变量的值,printf ...

  4. Oracle类型number与PG类型numeric对比和转换策略

    Oracle 11g number 任意精度数字类型 http://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#CNCPT3 ...

  5. JS对象添加新的字段

    var test={name:"name",age:"12"}; test.id = "12345"; 直接定义添加就成了

  6. poj_1442 Treap

    Treap是一种动态平衡二叉树结构,具有期望的O(log2n)的复杂度.适用于动态区间数据的查询.更改.维护等操作. 题目大意 一组数从前向后插入队列中,插入的过程中会有查询,查询当前队列中的第k小的 ...

  7. 日记整理---->2016-11-01

    这里我们整理一下项目的流程,一般来说做一个模块之前.会有需求文档.页面原型和接口文档. 一. js获取radio的值 页面的html代码: <ul class="list-group& ...

  8. 卸载vue-cli

    全局安装:npm install vue-cli -g; 全局卸载:npm uninstall vue-cli -g; 查看vue版本,vue -V 回车,查看vue最新的版本.

  9. sencha touch 入门系列 (一)sencha touch 简介

    参考链接:http://mobile.51cto.com/others-278381.htm Sencha touch 是基于JavaScript编写的Ajax框架ExtJS,将现有的ExtJS整合J ...

  10. Nodejs Web模块( readFile 根据请求跳转到响应html )

    index.js 根据请求的路径pathname,返回响应的页面. var http = require('http'); var fs = require('fs'); var url = requ ...