问题
Perhaps I'm over thinking this but consider the following example:
bool some_state = false;
// ... later ...
some_state = true;
do_something();
some_state = false;
Now imagine that do_something()
can throw. We won't set some_state
back to false
. What would be nice is to have some sort of automatic stack that pushes/pops based on scope for remembering previous values:
{
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)
Does boost have something like this? I can write my own object of course but I want to make sure I'm not reinventing the wheel first. I'm using C++03 on MSVC, so I can't use any fancy new C++11 unfortunately :(
回答1:
Boost does have something like this. It's called state_saver. It's kind of buried in the serialization library, but it is documented and apparently official (i.e. not in some detail namespace).
http://www.boost.org/doc/libs/1_56_0/libs/serialization/doc/state_saver.html
Demo: http://rextester.com/NVXUG70771
回答2:
You're barking up the right tree. Bjarne Stroustrup strongly recommends RAII for exception handling as opposed to try/catch/finally. In the latest edition of The C++ Programming Language (4th edition), he completely outlines his recommended method in Chapter 13, Exception Handling.
It's pretty hard to substitute an entire chapter, so first off, I'd recommend just reading the chapter. However, the essential idea is to use composition and let the constructor secure the resource.
So, if you have class A that secures 3 resources that may each throw (perhaps some memory), you instead let the subobject secure each in its constructor (not A's constructor). The key point is that, if the constructor is allowed to complete, it is guaranteed (by the language) that the destructor will be invoked. So, in the top-level constructor initialize the subobjects like this:
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;
}
Just imagine that class C and class D exist and they are structured like B. So, in your example above, some_state will be secured via a class like B, C or D.
Another key point here. You should only secure a single resource in each subobject's class. That way, the resource gets acquired and the constructor is allowed to exit (thus ensuring the destructor gets invoked which will safely release the resource) or it throws (therefore, not acquiring the resource).
来源:https://stackoverflow.com/questions/17822788/raii-object-for-restoring-previous-value