【问题标题】:RAII object for restoring previous value用于恢复先前值的 RAII 对象
【发布时间】:2013-07-23 23:37:01
【问题描述】:

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

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

【问题讨论】:

  • 更多的是编码风格问题,而不是语言本身。只需使用try/catch...
  • @billz try/catch 用于异常处理,不是正常的预期逻辑。投掷也有性能影响。不是一个可接受的解决方案。 RAII 不是一种编码风格偏好,而是一种设计模式。
  • Boost.ScopeExit,但我不确定这是否合适。用法:BOOST_SCOPE_EXIT(some_state = true);
  • 是的,OP 只想要一个范围保护,而 ScopeExit 似乎是一个范围保护。
  • @Robert Dailey 1.37 5 岁...

标签: c++ boost c++03


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    你在正确的树上吠叫。 Bjarne Stroustrup 强烈建议将 RAII 用于异常处理,而不是 try/catch/finally。在最新一期的 The C++ Programming Language(第 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 之类的类来保护。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-25
      相关资源
      最近更新 更多