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