zoukankan      html  css  js  c++  java
  • c++11 实现RAII特性

     参考文章https://blog.csdn.net/pongba/article/details/7911997

    什么是RAII 技术?(参见百度百科相关条目)

          RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。 
      RAII 的一般做法是这样的:在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处: 

      • 不需要显式地释放资源。 
      • 采用这种方式,对象所需的资源在其生命期内始终保持有效。

    之前的一篇文章中,实现的一个锁即符合RAII

    下面介绍一种方式来实现异常安全

    例子1.如下代码

    HANDLE h = CreateFile(...);
    closehandle(h);

    如果第一行出现异常,则会直接跳过closehandle,下面介绍的方法则简单方便的防止这种情况的出现

    class ScopeGuard
    {
    public:
        explicit ScopeGuard(std::function<void()> onExitScope) 
            : onExitScope_(onExitScope), dismissed_(false)
        { }
    
        ~ScopeGuard()
        {
            if(!dismissed_)
            {
                onExitScope_();
            }
        }
    
        void Dismiss()
        {
            dismissed_ = true;
        }
    
    private:
        std::function<void()> onExitScope_;
        bool dismissed_;
    
    private: // noncopyable
        ScopeGuard(ScopeGuard const&);
        ScopeGuard& operator=(ScopeGuard const&);
    };

    这个类的使用很简单,你交给它一个std::function,它负责在析构的时候执行,绝大多数时候这个function就是lambda,例如:

    HANDLE h = CreateFile(...);
    ScopeGuard onExit([&] { CloseHandle(h); });
    

    onExit在析构的时候会忠实地执行CloseHandle。为了避免给这个对象起名的麻烦(如果有多个变量,起名就麻烦大了),可以定义一个宏,把行号混入变量名当中,这样每次定义的ScopeGuard对象都是唯一命名的。

    #define SCOPEGUARD_LINENAME_CAT(name, line) name##line
    #define SCOPEGUARD_LINENAME(name, line) SCOPEGUARD_LINENAME_CAT(name, line)
    
    #define ON_SCOPE_EXIT(callback) ScopeGuard SCOPEGUARD_LINENAME(EXIT, __LINE__)(callback)
    

    Dismiss()函数也是Andrei的原始设计的一部分,其作用是为了支持rollback模式,例如:

    ScopeGuard onFailureRollback([&] { /* rollback */ });
    ... // do something that could fail
    onFailureRollback.Dismiss();
    

    在上面的代码中,“do something”的过程中只要任何地方抛出了异常,rollback逻辑都会被执行。如果“do something”成功了,onFailureRollback.Dismiss()会被调用,设置dismissed_为true,阻止rollback逻辑的执行。

    ScopeGuard是资源自动释放,以及在代码出错的情况下rollback的不可或缺的设施,C++98由于没有lambda和tr1::function的支持,ScopeGuard不但实现复杂,而且用起来非常麻烦,陷阱也很多,而C++11之后立即变得极其简单,从而真正变成了每天要用到的设施了。C++的RAII范式被认为是资源确定性释放的最佳范式(C#的using关键字在嵌套资源申请释放的情况下会层层缩进,相当的不能scale),而有了ON_SCOPE_EXIT之后,在C++里面申请释放资源就变得非常方便

    Acquire Resource1
    ON_SCOPE_EXIT( [&] { /* Release Resource1 */ })
    
    Acquire Resource2
    ON_SCOPE_EXIT( [&] { /* Release Resource2 */ })
    …
    

    这样做的好处不仅是代码不会出现无谓的缩进,而且资源申请和释放的代码在视觉上紧邻彼此,永远不会忘记。更不用说只需要在一个地方写释放的代码,下文无论发生什么错误,导致该作用域退出我们都不用担心资源不会被释放掉了。我相信这一范式很快就会成为所有C++代码分配和释放资源的标准方式,因为这是C++十年来的演化所积淀下来的真正好的部分之一。

  • 相关阅读:
    产品逻辑中的—B端技术常识:同步异步接口模式
    高德地图0907
    高德地图出异常
    svn提交报错
    高德地图demo
    JS中一些特殊的方法
    video标签的学习
    document.getElementById('file').files[0]的jquery写法
    layer请求设置遮罩
    easypoi入门<1
  • 原文地址:https://www.cnblogs.com/wangshaowei/p/9429260.html
Copyright © 2011-2022 走看看