首页  >  文章  >  后端开发  >  以下是一些标题选项,每个标题都强调文章的不同方面: 选项 1:重点关注概念和 C 11 功能: * C 11 中的 ScopeGuard:简单的错误处理,但有哪些注意事项

以下是一些标题选项,每个标题都强调文章的不同方面: 选项 1:重点关注概念和 C 11 功能: * C 11 中的 ScopeGuard:简单的错误处理,但有哪些注意事项

Patricia Arquette
Patricia Arquette原创
2024-10-28 04:51:02648浏览

Here are a few title options, each emphasizing a different aspect of the article:

Option 1: Focusing on the concept and C  11 feature:

* ScopeGuard in C  11: Simple Error Handling, But Which Caveats? 

Option 2: Highlighting the simplicity and limitatio

最简单、最简洁的 C 11 ScopeGuard

在 C 11 中,有一个简单的习惯用法可以实现 ScopeGuard,从而简化错误和资源处理。下面是一个简单的解释和实现:

概念:

ScopeGuard 是一个 C 类,它提供了一种定义代码块的方法,当创建守卫的范围退出。这样可以轻松进行清理和错误处理,确保即使在特殊情况下也能释放资源或采取操作。

实现:

<code class="cpp">namespace RAII
{
    template< typename Lambda >
    class ScopeGuard
    {
        mutable bool committed;
        Lambda rollbackLambda; 
        public:

            ScopeGuard( const Lambda&amp; _l) : committed(false) , rollbackLambda(_l) {}

            template< typename AdquireLambda >
            ScopeGuard( const AdquireLambda&amp; _al , const Lambda&amp; _l) : committed(false) , rollbackLambda(_l)
            {
                _al();
            }

            ~ScopeGuard()
            {
                if (!committed)
                    rollbackLambda();
            }
            inline void commit() const { committed = true; }
    };

    template< typename aLambda , typename rLambda>
    const ScopeGuard< rLambda >&amp; makeScopeGuard( const aLambda&amp; _a , const rLambda&amp; _r)
    {
        return ScopeGuard< rLambda >( _a , _r );
    }

    template<typename rLambda>
    const ScopeGuard< rLambda >&amp; makeScopeGuard(const rLambda&amp; _r)
    {
        return ScopeGuard< rLambda >(_r );
    }
}</code>

使用:

<code class="cpp">void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptions() 
{
   std::vector<int> myVec;
   std::vector<int> someOtherVec;

   myVec.push_back(5);
   //first constructor, adquire happens elsewhere
   const auto&amp; a = RAII::makeScopeGuard( [&amp;]() { myVec.pop_back(); } );  

   //sintactically neater, since everything happens in a single line
   const auto&amp; b = RAII::makeScopeGuard( [&amp;]() { someOtherVec.push_back(42); }
                     , [&amp;]() { someOtherVec.pop_back(); } ); 

   b.commit();
   a.commit();
}</code>

在此示例中,ScopeGuard 习惯用法用于确保即使在函数范围内引发异常,也会执行清理操作(从向量中弹出元素)。

简单性和局限性:

ScopeGuard 习惯用法的这种实现相对简单和简洁,使其易于使用和理解。然而,原问题的作者也对潜在的缺陷或缺失的专业提出了担忧。让我们解决这些问题:

  • 完美转发: 该实现并未充分利用 C 11 完美转发技术。这在 makeScopeGuard 函数中很明显,该函数使用左值引用而不是 lambda 参数的通用引用。使用通用引用将提供更大的灵活性并支持右值 lambda。
  • 如果获取抛出异常则回滚: 原始实现不处理“获取”lambda 抛出异常的情况。如果获取 lambda 失败,则永远不会调用回滚 lambda,从而使代码处于不一致的状态。

为了解决这些问题,以下是 ScopeGuard 实现和用法的更新版本:

<code class="cpp">namespace RAII
{
    template< typename Lambda >
    class ScopeGuard
    {
        bool committed;
        Lambda rollbackLambda; 
        public:

            ScopeGuard( const Lambda&amp; _l) : committed(false) , rollbackLambda(_l) {}

            ScopeGuard( const ScopeGuard&amp; _sc) : committed(false) , rollbackLambda(_sc.rollbackLambda) 
            {
                if (_sc.committed)
                   committed = true;
                else
                   _sc.commit();
            }

            ScopeGuard( ScopeGuard&amp;&amp; _sc) : committed(false) , rollbackLambda(_sc.rollbackLambda)
            {
                if (_sc.committed)
                   committed = true;
                else
                   _sc.commit();
            }

            //WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
            template< typename AdquireLambda >
            ScopeGuard( const AdquireLambda&amp; _al , const Lambda&amp; _l) : committed(false) , rollbackLambda(_l)
            {
               std::forward<AdquireLambda>(_al)();
            }

            //WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
            template< typename AdquireLambda, typename L >
            ScopeGuard( AdquireLambda&amp;&amp; _al , L&amp;&amp; _l) : committed(false) , rollbackLambda(std::forward<L>(_l))
            {
                std::forward<AdquireLambda>(_al)(); // just in case the functor has &amp;&amp;-qualified operator()
            }


            ~ScopeGuard()
            {
                if (!committed)
                    rollbackLambda();
            }
            inline void commit() { committed = true; }
    };


    //WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
    template< typename aLambda , typename rLambda>
    ScopeGuard< rLambda > // return by value is the preferred C++11 way.
    makeScopeGuardThatDoesNOTRollbackIfAdquireThrows( aLambda&amp;&amp; _a , rLambda&amp;&amp; _r) // again perfect forwarding
    {
        return ScopeGuard< rLambda >( std::forward<aLambda>(_a) , std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
    }

    template< typename aLambda , typename rLambda>
    ScopeGuard< rLambda > // return by value is the preferred C++11 way.
    makeScopeGuardThatDoesRollbackIfAdquireThrows( aLambda&amp;&amp; _a , rLambda&amp;&amp; _r) // again perfect forwarding
    {
        auto scope = ScopeGuard< rLambda >(std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
        _a();
        return scope;
    }

    template<typename rLambda>
    ScopeGuard< rLambda > makeScopeGuard(rLambda&amp;&amp; _r)
    {
        return ScopeGuard< rLambda >( std::forward<rLambda>(_r ));
    }

    namespace basic_usage
    {
        struct Test
        {

            std::vector<int> myVec;
            std::vector<int> someOtherVec;
            bool shouldThrow;
            void run()
            {
                shouldThrow = true;
                try
                {
                    SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows();
                } catch (...)
                {
                    AssertMsg( myVec.size() == 0 &amp;&amp; someOtherVec.size() == 0 , "rollback did not work");
                }
                shouldThrow = false;
                SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows();
                AssertMsg( myVec.size() == 1 &amp;&amp; someOtherVec.size() == 1 , "unexpected end state");
                shouldThrow = true;
                myVec.clear(); someOtherVec.clear();  
                try
                {
                    SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesRollbackIfAdquireThrows();
                } catch (...)
                {
                    AssertMsg( myVec.size() == 0 &amp;&amp; someOtherVec.size() == 0 , "rollback did not work");
                }
            }

            void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows() //throw()
            {

                myVec.push_back(42);</code>

以上是以下是一些标题选项,每个标题都强调文章的不同方面: 选项 1:重点关注概念和 C 11 功能: * C 11 中的 ScopeGuard:简单的错误处理,但有哪些注意事项的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn