什么是C++中的ScopeGuard?

bea*_*man 8 c++

我的印象是它是由第三方编写的库中包含的C++类.我尝试在Google上搜索,我发现有一篇帖子说使用它是个好主意.但是,它未能准确描述它是什么以及如何将其合并到我的代码中.谢谢.

Che*_*Alf 16

ScopeGuard曾经是Petru Marginean和Andrei Alexandrescu特别实施的守卫.我们的想法是让一个保护对象的析构函数在一个作用域的末尾调用一个用户指定的清理操作(读取:块),除非取消范围保护.Marginean提出了一个巧妙的想法,即基于对引用的生命周期扩展,为C++ 03声明一个范围保护对象const.

今天"范围守卫"更是一般的想法.

范围保护基于RAII(用于清理的自动析构函数调用),就像一个for循环基于跳转一样,但是通常不会将for循环称为基于跳转的代码,因为它丢失了大部分信息.它是什么意思,同样通常也不会将范围保护称为RAII.for循环处于更高的抽象层次,并且是比跳转更专业的概念.范围保护处于更高的抽象层次,并且是比RAII更专业的概念.


在C++ 11中,范围保护可以简单地实现,std::function通过lambda表达式在每个地方提供清理操作.

例:

#include <functional>       // std::function
#include <utility>          // std::move

namespace my {
    using std::function;
    using std::move;

    class Non_copyable
    {
    private:
        auto operator=( Non_copyable const& ) -> Non_copyable& = delete;
        Non_copyable( Non_copyable const& ) = delete;
    public:
        auto operator=( Non_copyable&& ) -> Non_copyable& = default;
        Non_copyable() = default;
        Non_copyable( Non_copyable&& ) = default;
    };

    class Scope_guard
        : public Non_copyable
    {
    private:
        function<void()>    cleanup_;

    public:
        friend
        void dismiss( Scope_guard& g ) { g.cleanup_ = []{}; }

        ~Scope_guard() { cleanup_(); }

        template< class Func >
        Scope_guard( Func const& cleanup )
            : cleanup_( cleanup )
        {}

        Scope_guard( Scope_guard&& other )
            : cleanup_( move( other.cleanup_ ) )
        { dismiss( other ); }
    };

}  // namespace my

#include <iostream>
void foo() {}
auto main() -> int
{
    using namespace std;
    my::Scope_guard const final_action = []{ wclog << "Finished! (Exit from main.)\n"; };

    wcout << "The answer is probably " << 6*7 << ".\n";
}
Run Code Online (Sandbox Code Playgroud)

的作用function在这里是为了避免模板,这样Scope_guard情况下,可以声明为这样的,和周围通过.另一种稍微复杂且使用稍微有限但可能稍微更有效的方法是使用仿函数类型模板化类,并使用C++ 11 auto进行声明,并使用工厂函数创建范围保护实例.这两种技术都是简单的C++ 11方法,可以完成Marginean对C++ 03的参考生命周期扩展所做的工作.


Ian*_*ney 7

它比特定类更像是一种设计模式。这是一种获取/释放异常安全的资源(例如文件,内存或互斥锁)的方法。unique_lock在c ++ 11中遵循这种模式。

例如,使用unique_lock,而不是像这样编写代码:

void foo()
{
    myMutex.lock();
    bar();
    myMutex.unlock();
}
Run Code Online (Sandbox Code Playgroud)

您编写如下代码:

void foo()
{
    unique_lock<mutex> ulock(myMutex);
    bar();
}
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,如果bar引发异常该怎么办?然后,myMutex将永远不会被解锁,并且您的程序将处于无效状态。但是,在第二种情况下,unique_lock已编程为将互斥锁锁定在其构造函数中,并在其析构函数中对其进行解锁。即使bar抛出异常,unique_lock当异常向上传播时,栈将解散,也会破坏,因此锁将被释放。这省去了将每个调用包装bar在try / catch块中并手动处理异常的麻烦。