C++之RAII机制

4 篇文章 1 订阅

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值