210 likes | 272 Views
Understand RAII pattern, examples, exception safety, and the benefits of automatic resource management. Learn the solution to matching call pairs and knowing if a resource is initialized. Implement RAII for error-free code in C++.
E N D
RAII Resource Allocation IS initialization
Agenda • The RAII pattern • Examples • Exception safety
The problem • new/delete • aquire/unaquire • connect/disconnect • lock/unlock • subscribe/unsubscribe • register/unregister • open/close
The problem Need to match call pairs Need to know if a resource is initialized
The solution Link call pair to an object’s life time • Constructor: Aquire resource • Destructor: Release resource
Stackbased example void monkey_func() { ... MyResource res; // May throw ... do_stuff(res); ... } // Automatically releases res
Without RAII class Foo { class Listener { virtual void Bar() = 0; }; void subscribe(Listener& l); void unsubscribe(Listener& l); };
Using RAII class Foo { class Listener { virtual void Bar() = 0; }; class SubscriptionToken { SubscriptionToken(Foo& f, Listener& l); }; };
Example usage code class ConstantFooListener : public Foo::Listener { Dummy(Foo& f) : token_(f, *this) {} Foo::SubscriptionToken token_; }
Why the strange acronym? No two-stage initialization class SomeResource { SomeResource(...); bool Initialize(...); };
What are the benefits? • No uninitialized resources • Close impossible to mismatch call pairs • Exception safe!
Sidetrack: Exception safe code How to implement commit/rollback
The problem Operation • Subsequent calls which might throw • Some steps need to be undone The whole operation should succeed or fail
Example problem prepare_something(); possibly_throwing_call_1(); possibly_throwing_call_2(); rollback_preparation(); rollback_call_1();
Naive approach prepare_something(); try { possibly_throwing_call_1(); try { possibly_throwing_call_2(); } catch (...) { rollback_call_1(); rollback_preparation(); throw; } } catch (...) { rollback_preparation(); throw; }
Problem with naive approach • Error-prone to write • Difficult to read • Difficult to change • Poor scalability
A better solution Suggestions?
A RAII similar solution Half of RAII, just the cleanup part Object whose destructor rollbacks change Commit by dismissing rollback
A general object class ScopeGuard { ScopeGuard(const boost::function<void()>& rollback) : mRollBack(rollback) {} ~ScopeGuard() { if (mRollBack) mRollBack(); } void Dismiss() { mRollback = boost::function<void()>(); } private: boost::function<void()> mRollback; };
ScopeGuard example prepare_something(); ScopeGuard preparation_guard(rollback_preparation); possibly_throwing_call_1(); ScopeGuard call_1_guard(rollback_call_1); possibly_throwing_call_2(); // Commit preparation_guard.Dismiss(); call_1_guard.Dismiss();