RAII for other resources

The name, RAII, refers to resources and not memory, and indeed the same approach is applicable to other resources. For each resource type, we need a special object, although generic programming and lambda expressions may help us to write less code (we will learn more on this in Chapter 11ScopeGuard).  The resource is acquired in the constructor and released in the destructor. Note that there are two slightly different flavors of RAII. The first option is the one we have already seen—the actual acquisition of the resource is at initialization, but outside of the constructor of the RAII object.

The constructor merely captures the handle (such as a pointer) that resulted from this acquisition. This was the case with the scoped_ptr object that we just saw—memory allocation and object construction were both done outside of the constructor of the scoped_ptr object, but still during its initialization. The second option is for the constructor of the RAII object to actually acquire the resource. Let's see how this works, with the example of a RAII object that manages mutex locks:

class mutex_guard {
public:
explicit mutex_guard(std::mutex& m) : m_(m) { m_.lock(); }
~mutex_guard() { m_.unlock(); }
private:
std::mutex& m_;
};

Here, the constructor of the mutex_guard class itself acquires the resource; in this case, exclusive access to the shared data protected by the mutex. The destructor releases that resource. Again, this pattern completely removes the possibility of leaking a lock (that is, exiting a scope without releasing the lock), for example, when an exception is thrown:

std::mutex m;
TEST(Scoped_ptr, ThrowNoLeak) {
try {
mutex_guard lg(m);
EXPECT_FALSE(m.try_lock()); // Expect to be locked already
throw 1;
} catch ( ... ) {
}
EXPECT_TRUE(m.try_lock()); // Expect to be unlocked
m.unlock(); // try_lock() will lock, undo it
}

In this test, we check whether the mutex is locked or not by calling std::mutex::try_lock()—we cannot call lock() if the mutex is already locked, as it will deadlock. By calling try_lock(), we can check the state of the mutex without the risk of deadlock (but remember to unlock the mutex if try_lock() succeeds).

Again, the standard provides a RAII object for mutex locking, std::lock_guard. It is used in a similar manner, but can be applied to any mutex type that has lock() and unlock() member functions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.33.207