For example, suppose you’re using a C API to manipulate mutex objects of type Mutex offering functions lock and unlock:
void lock(Mutex *pm); // lock mutex pointed to by pm void unlock(Mutex *pm); // unlock the mutex
To make sure that you never forget to unlock a Mutex you’ve locked, you’d like to create a class to manage locks. The basic structure of such a class is dictated by the RAII(Resource Acquisition Is Initialization) principle that resources are acquired during construction and released during destruction:
class Lock { public: explicit Lock(Mutex *pm) : mutexPtr(pm) { lock(mutexPtr); } // acquire resource ~Lock() { unlock(mutexPtr); } // release resource private: Mutex *mutexPtr; };
Clients use Lock in the conventional RAII fashion:
Mutex m; // define the mutex you need to use create block to define critical section //... { Lock ml(&m); // lock the mutex perform critical section operations automatically unlock mutex at end of block // ... }
This is fine, but what should happen if a Lock object is copied?
Lock ml1(&m); // lock m Lock ml2(ml1); // copy ml1 to ml2 — what should happen here?
Most of the time, you’ll want to choose one of the following possibilities:
1. Prohibit copying
class Lock : private Uncopyable { // prohibit copying — see Item 6 as before public: //... };
2. Reference-count the underlying resource
Often, RAII classes can implement reference-counting copying behavior by containing a tr1::shared_ptr data member.
Unfortunately, tr1::shared_ptr’s default behavior is to delete what it points to when the reference count goes to zero, and that’s not what we want.
Fortunately, tr1::shared_ptr allows specification of a “deleter” — a function or function object to be called when the reference count goes to zero. (This functionality does not exist for auto_ptr, which always deletes its pointer.)
class Lock { public: explicit Lock(Mutex *pm) // init shared_ptr with the Mutex to point to and the unlock func as the deleter : mutexPtr(pm, unlock) { lock(mutexPtr.get()); // see Item 15 for info on "get" } private: std::tr1::shared_ptr<Mutex> mutexPtr; // use shared_ptr instead of raw pointer };
Things to Remember:
- Copying an RAII object entails copying the resource it manages, so the copying behavior of the resource determines the copying behavior of the RAII object.
- Common RAII class copying behaviors are disallowing copying and performing reference counting, but other behaviors are possible.