RAII对象,用于恢复以前的值

RAII object for restoring previous value

本文关键字:恢复 对象 用于 RAII      更新时间:2023-10-16

也许我想得太多了,但请考虑以下示例:

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:(

Boost确实有类似的东西。它被称为state_saver。它有点埋在序列化库中,但它是有文档记录的,而且显然是官方的(即不在一些详细的命名空间中)。

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

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

你说得对。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来保护。

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