用于恢复先前值的RAII对象

时间:2013-07-23 23:37:01

标签: c++ boost c++03

也许我在考虑这个问题,但请考虑以下示例:

bool some_state = false;

// ... later ...

some_state = true;
do_something();
some_state = false;

现在假设do_something()可以投掷。我们不会将some_state设置回false。什么是好的是有一些自动堆栈,基于范围推送/弹出记住以前的值:

{
    scoped_restore res( some_state, true ); // This sets some_state to true and remembers previous value (false)
    do_something();
} // At this point, res is destroyed and sets some_state back to false (previous value)

boost有这样的东西吗?我当然可以写自己的对象,但我想确保我不是先重新发明轮子。我在MSVC上使用C ++ 03,所以不幸的是我不能使用任何花哨的新C ++ 11 :(

2 个答案:

答案 0 :(得分:1)

Boost 确实有这样的东西。它被称为state_saver。它被隐藏在序列化库中,但是它被记录下来并且显然是官方的(即不在某些细节命名空间中)。

http://www.boost.org/doc/libs/1_56_0/libs/serialization/doc/state_saver.html

演示:http://rextester.com/NVXUG70771

答案 1 :(得分:0)

你正在咆哮着正确的树。 Bjarne Stroustrup强烈建议使用RAII进行异常处理而不是try / catch / finally。在最新版的“C ++编程语言”(第4版)中,他完全概述了第13章“异常处理”中推荐的方法。

很难替换整个章节,所以首先,我建议只阅读本章。但是,基本思想是使用组合并让构造函数保护资源。

所以,如果你有一个A类来保护每个可能抛出的3个资源(可能是一些内存),你可以让子对象在它的构造函数(而不是A的构造函数)中保护它们。关键点在于,如果允许构造函数完成,则可以保证(通过语言)将调用析构函数。因此,在顶层构造函数中初始化子对象,如下所示:

class B{

    public:

        B( int n )
        {
            //allocate memory (may throw)
        }

        void *secured_memory;

        ~B(){
            //release memory        
        }

}


class A{

    public:

        A( int n )
            :b{n}, c{n}, d{n}
        {
            //will not complete if B, C or D throws
            //but because the constructors of B and C completed
            //their destructors will be invoked if D throws         
        }

        B b;
        C c;
        D d;

}

想象一下C类和D类存在,它们的结构类似于B.因此,在上面的例子中,some_state将通过类似B,C或D的类来保护。

这里的另一个关键点。您应该只保护每个子对象的类中的单个资源。这样,获取资源并允许构造函数退出(从而确保调用析构函数将安全地释放资源)或抛出(因此,不获取资源)。