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 */


/// inlins
inline RAII& RAII::commit(bool c = true) noexcept
{
_commit = c; return *this;
};


/// 用于实体资源的RAII管理类
/// T为资源类型
/// acquire为申请资源动作,返回资源T
/// release为释放资源动作,释放资源T
template<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