C++之RAII機制

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章