1.什么是RAII?
RAII(Resource Acquisition Is Initialization)机制是Bjarne Stroustrup首先提出的,也称直译为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的机制。
C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。
RAII 机制就是利用了C++的上述特性,在需要获取使用资源RES的时候,构造一个临时对象(T),在其构造T时获取资源,在T生命期控制对RES的访问使之始终保持有效,最后在T析构的时候释放资源。以达到安全管理资源对象,避免资源泄漏的目的。
2.为什么要使用RAII?
那么我们经常所说的资源是如何定义的?说到资源,我们立刻能想到的就是内存啊,文件句柄等等啊,但只有这些吗?
对于资源的概念不要想象的太狭隘,在计算机系统中,资源是个定义宽泛的概念,所有数量有限且对系统正常运行具有一定作用的元素都是资源。比如:网络套接字、互斥锁、文件句柄、内存、数据库记录等等,它们属于系统资源。由于系统的资源是有限的,就好比自然界的石油,铁矿一样,不是取之不尽,用之不竭的。
所以,我们在编程使用系统资源时,都必须遵循一个步骤:
1.申请资源;
2.使用资源;
3.释放资源。
第一步和第三步缺一不可,因为资源必须要申请才能使用的,使用完成以后,必须要释放,如果不释放的话,就会造成资源泄漏。
3.实战应用
3.1一个简单的例子:指针申请空间,释放空间
void Func()
{ int *ip = new int[10]; //operations //operations //operations delete[] ip;//if not free mem, memory overflow
}
使用RAII技术后:
template<class PointerType>
class My_Pointer
{
public: My_Pointer(PointerType* _ptr, size_t sz) { _ptr = new PointerType[sz]; m_ptr = _ptr; } ~My_Pointer() { delete []m_ptr; }
protected: PointerType m_ptr;
}
3.2 scope lock (局部锁技术)
在很多时候,为了实现多线程之间的数据同步,我们会使用到 mutex,critical section,event,singal 等技术。但在使用过程中,由于各种原因,有时候,我们会遇到一个问题:由于忘记释放(Unlock)锁,产生死锁现象。
采用RAII 就可以很好的解决这个问题,使用着不必担心释放锁的问题. 示例代码如下:
My_scope_lock 为实现 局部锁的模板类.
LockType 抽象代表具体的锁类 .如基于 mutex 实现 mutex_lock 类
template<class LockType>
class My_scope_lock
{
public: My_scope_lock(LockType& _lock):m_lock(_lock) { m_lock.occupy(); } ~My_scope_lock() { m_lock.relase(); }
protected: LockType m_lock;
}
使用的时候:
//global vars
int counter = 0;
void routine();
mutex_lock m_mutex_lock;
void routine()
{ My_scope_lock l_lock(m_mutex_lock); counter++; //others...
}
我们可以根据上面的例子类推出好多这样例子。如读写文件的时候很容易忘记关闭文件,如果借用 RAII技术,就可以规避这种错误。再如对数据库的访问,忘记断开数据库连接等等都可以借助RAII 技术也解决。
4.RAII模板化实现
按照上节的做法,如果你有很多个资源对象要用RAII方式管理,按这个办法就要为每个类写一个RAII类。
想到这里,我瞬间觉得好烦燥,都是类似的代码,却要一遍一遍的重复,就不能有个通用的方法让我少写点代码嘛!!!
于是我利用C++11的新特性(类型推导、右值引用、移动语义、类型萃取、function/bind、lambda表达式等等)写了一个通用化的RAII机制,满足各种类型资源的管理需求。
//
// RAII.h
//
// Library: Foundation
// Package: Core
// Module: RAII
//
// Definition of the RAII template class and friends.
//
//#include "Wishbone/Foundation.h"
#include <type_traits>
#include <functional>namespace Wishbone
{/* 元模板,如果是const类型则去除const修饰符 */template<typename T>struct no_const{using type = typename std::conditional<std::is_const<T>::value, typename std::remove_const<T>::type, T>::type;};/// RAII方式管理申请和释放资源的类/// 对象创建时,执行acquire(申请资源)动作(可以为空函数[]{})/// 对象析构时,执行release(释放资源)动作/// 禁止对象拷贝和赋值class RAII{public:typedef std::function<void()> FunctionType;/// release: 析构时执行的函数/// acquire: 构造函数执行的函数/// default_com:_commit,默认值,可以通过commit()函数重新设置explicit RAII(FunctionType release, FunctionType acquire = [] {}, bool default_com = true) noexcept :_commit(default_com),_release(release){acquire();}/// 对象析构时根据_commit标志执行_release函数~RAII() noexcept{if (_commit)_release();}/// 移动构造函数 允许右值赋值RAII(RAII&& rv) noexcept : _commit(rv._commit),_release(rv._release){rv._commit = false;};///RAII& commit(bool c = true) noexcept;protected:std::function<void()> _release;private:RAII(const RAII&);RAII& operator=(const RAII&) = delete;bool _commit;}; /* RAII *//// inlinsinline RAII& RAII::commit(bool c = true) noexcept{ _commit = c; return *this; };/// 用于实体资源的RAII管理类/// T为资源类型/// acquire为申请资源动作,返回资源T/// release为释放资源动作,释放资源Ttemplate<typename T>class RAIIVar{public:typedef std::function<T()> AcquireType;typedef std::function<void(T &)> ReleaseType;///explicit RAIIVar(AcquireType acquire, ReleaseType release) noexcept :_resource(acquire()),_release(release){}/// 移动构造函数RAIIVar(RAIIVar&& rv) :_resource(std::move(rv._resource)),_release(std::move(rv._release)){rv._commit = false;//控制右值对象析构时不再执行_release}/// 对象析构时根据_commit标志执行_release函数~RAIIVar() noexcept{if (_commit)_release(_resource);}RAIIVar<T>& commit(bool c = true) noexcept{ _commit = c; return *this;};T& get() noexcept{return _resource; }T& operator*() noexcept{return get();}template<typename _T = T>typename std::enable_if<std::is_pointer<_T>::value, _T>::type operator->() const noexcept{return _resource;}template<typename _T = T>typename std::enable_if<std::is_class<_T>::value, _T*>::type operator->() const noexcept{return std::addressof(_resource);}private:bool _commit = true;T _resource;ReleaseType _release;};/// 创建 RAII 对象,/// 用std::bind将M_REL,M_ACQ封装成std::function<void()>创建RAII对象/// RES 资源类型/// M_REL 释放资源的成员函数地址/// M_ACQ 申请资源的成员函数地址template<typename RES, typename M_REL, typename M_ACQ>RAII make_raii(RES & res, M_REL rel, M_ACQ acq, bool default_com = true) noexcept{static_assert(std::is_class<RES>::value, "RES is not a class or struct type.");static_assert(std::is_member_function_pointer<M_REL>::value, "M_REL is not a member function.");static_assert(std::is_member_function_pointer<M_ACQ>::value, "M_ACQ is not a member function.");assert(nullptr != rel && nullptr != acq);auto p_res = std::addressof(const_cast<typename no_const<RES>::type&>(res));return RAII(std::bind(rel, p_res), std::bind(acq, p_res), default_com);}template<typename RES, typename M_REL>RAII make_raii(RES & res, M_REL rel, bool default_com = true) noexcept{static_assert(std::is_class<RES>::value, "RES is not a class or struct type.");static_assert(std::is_member_function_pointer<M_REL>::value, "M_REL is not a member function.");assert(nullptr != rel);auto p_res = std::addressof(const_cast<typename no_const<RES>::type&>(res));return RAII(std::bind(rel, p_res), [] {}, default_com);}} // namespace Wishbone