2013-07-23 32 views
6

也许我是在想这一点,但考虑下面的例子:RAII对象恢复以前的值

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) 

确实提升有这样的事?我当然可以写自己的对象,但是我想确保我不会首先重新发明轮子。我在MSVC上使用C++ 03,所以我不能使用任何奇特的新的C++ 11:(

+0

它,而不是语言本身更编码风格的问题。只需使用'try/catch' ... – billz

+3

@billz try/catch用于异常处理,不是正常的,预期的逻辑。投掷也有性能影响。不可接受的解决方案。 RAII不是一种编码风格偏好,它是一种设计模式。 –

+2

还有[Boost.ScopeExit](http://www.boost.org/doc/libs/1_39_0/libs/scope_exit/doc/html/index.html),但我不确定是否适合。用法:'BOOST_SCOPE_EXIT(some_state = true);' –

回答

0

你正在吠叫正确的树Bjarne Stroustrup强烈建议RAII进行异常处理在最新版的C++编程语言(第4版)中,他完全概述了他在第13章异常处理中推荐的方法。

要替换整个章节很难,所以首先关闭,我建议你只是阅读这一章,但是,基本的想法是使用合成,并让构造函数保护资源。因此,如果你有类A保护可能每个抛出的3个资源(perh aps一些内存),而是让子对象在其构造函数中保护每个(不是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.

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