C++11的资源管理:泛化的RAII

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++11的资源管理:泛化的RAII相关的知识,希望对你有一定的参考价值。

  RAII被认为是c++资源管理的最佳范式,但是c++98中用RAII必须为要管理的资源写一个类,这样一来RAII的使用就有些繁琐了。C++11有了lambda和function后,我们就可以编写泛化的RAII,实现ScopeGuard,优雅地解决这个问题。本文主要参考刘未鹏的博客

  主要代码如下 

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&);
};

为了在管理多个资源时不用花精力想变量名,可以定义几个宏

#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)

其中##是连接符,__LINE__是编译器自带的宏,指的是源代码的行号。这样就会自动生成变量名为"EXIT行号"。

应用如下

class A {
public:
    int a;
    A(int i) :a(i) {}
};

void EraseA(A &a)
{
    a.a = 0;
    cout << "eraseA" << endl;
}

int main()
{
    A a(2);
    ON_SCOPE_EXIT([&] { EraseA(a); });
    return 0;
}

 程序结束时输出eraseA。

Dismiss()函数作用是为了支持rollback模式,例如:

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

如果“do something”的代码出错,就执行析构函数中的rollback逻辑。如果代码顺利运行,就将dismissed_设为true,这样在退出作用域时就不会执行rollback操作了。

 lambda表达式用于创建匿名函数对象语法格式如下

[捕获列表] (函数参数) mutable或exception声明 ->返回类型 {函数体};

捕获列表是一个lambda所在函数中定义的局部变量的列表,可以为空,上文的[&]表示默认使用引用传值,如果用[=]则表示默认使用值传递。

最简单的使用方法如下

auto f = [] {return 42;}
cout<<f()<<endl;

 结果打印42。

以上是关于C++11的资源管理:泛化的RAII的主要内容,如果未能解决你的问题,请参考以下文章

C++RAII(Resource Acquisition Is Initialization 资源获取即初始化)是什么?(raii)

C++RAII机制(智能指针原理)

C++RAII机制(智能指针原理)

c_cpp RAII确保C ++ 11线程被加入

C++基于RAII对锁进行封装

[C++] 智能指针