Article 14: Beware of copying behavior in resource management classes
Think carefully about copying behavior in resource-managing classes
Article 13 introduces the idea that "the opportunity for resource acquisition is the time to initialize" (Resource acquisition is Initializaiton,raii), as a "resource management class" Spine, also describes Auto_ptr and tr1::shared _ptr How to express this idea in heap-based resources. However,
not all resources are heap-based, and smart pointers such as auto_ptr and tr1::shared_ptr are often unsuitable as resource holders for that resource.Case
Occasionally, you need to build your own resource management classes.
For example, suppose you use a C API function to handle a mutex object of type mutex, and a total of lock and unlock two functions are available:
void Lock (mutex* pm); Locks the mutex referred to by PM as void unlock (mutex* pm);
To ensure that you never forget to unlock a locked mutex, you might want to create a class to understand the lock. The basic structure of this class is governed by the RAII code, which is "resources obtained during construction and released during the destruction":
Class Lock {public: explicit Lock (mutex* pm): mutexptr (PM) { Lock (mutexptr); Get Resources } ~lock () { unlock (mutexptr); Release Resource }private:
Customer's use of lock is in accordance with RAII mode:
Mutex m; Define the required mutex ... { //Create a block to define critical section Lock M1 (&m); Lock Mutex ... Perform actions within critical section} //At the end of the block, automatically unlock the mutex
That's fine, but what happens if the lock object is assigned to a value?
Lock ML1 (&m); Lock Mlock ML2 (ML1); Copy the ML1 to ML2, what happens?
This is a specific example of a generalized problem.
"What happens when a Raii object is copied?", most of the time, the following two possibilities are selected:
1. Prohibit copyingMany times it is not reasonable to allow RAII objects to be copied. It is possible to have a class like lock, since it is very rare to have a copy of the synchronization infrastructure (synchronization primitives). If the copy action is to the Raii class Unreasonable, it should be prohibited. Clause 6 tells how to do this: Declare the copying action as private. It looks like this for lock:
Class Lock:private Uncopyable { //prohibit copying, see clause 6public:
2. The "Reference counting" method for the underlying resource(Reference-count). Sometimes you want to keep the resources until the last user of it is destroyed. In this case, when copying the Raii object, you should increment the "referenced number" of the resource. Tr1::shared_ptr.
Typically, a RAII class can implement reference-counting copying behavior as long as a TR1::SHARED_PTR member variable is included. If the above lock is intended to use reference counting. It can change the type of mutexptr, changing it from mutex* to Tr1::shared_ptr<mutex>
Unfortunately, the default behavior of Tr1::shared_ptr is "when the reference count is 0 o'clock to delete its object", it is not the desired behavior. When you use a Mutext, the release action that you want to make is unlocked rather than deleted.
Fortunately tr1::shared_ptr allows for the designation of the so-called "delete device (deleter)", which is a function or function object that is called when the reference count is 0 o'clock (this function does not exist in the auto_ptr--it always deletes its pointer). To TR1:: The shared_ptr constructor is a second parameter that is optional, so the code looks like this:
Class Lock {public: explicit Lock (mutex* pm)// initialized with a Mutex shared_ptr : Mutexptr (PM, unlock) // and take the unlock function as the delete { lock (Mutexptr.get ()); Clause 15 refers to "get" }private: //Replace raw pointer std::tr1::shared_ptr<mutex> mutexptr with shared_ptr;};
The lock class in this example no longer declares the destructor because it is not necessary. Clause 5 said that the class destructor (either compiler-generated or user-defined) automatically calls its Non-static member variable (in this case, mutexptr) Destructors. Mutexptr's destructor automatically calls the Tr1::shared_ptr's delete (in this case, unlock) when the reference count of the mutex is 0.
Copy the underlying resource. Sometimes, if you want, you can have any number of copies for a resource. The only reason to need a resource management class is to make sure that it is freed when a copy is no longer needed. In this case, copy the resource management object, you should also copy the resources that it wraps. When you copy a resource management object, you do a "deep copy."
Some standard string types are made up of pointers to heap memory (that memory is used to hold the constituent characters of the string). This string object contains a pointer to a piece of heap memory. When such a string object is copied, Either the pointer or the memory it refers to will be made a copy. Such a string shows deep copying behavior.
Transfer ownership of the bottom resource. In some cases, you might want to make sure that only one Raii object is always pointing to an unprocessed resource (raw resource), even if the Raii object is copied. The ownership of the resource is transferred from the copied to the target. As in clause 13, this is Auto_ PTR pursues the meaning of replication.
Copying functions, including the copy constructor and the copy assignment operator, can be created automatically by the compiler, so you have to write them yourself unless the compiler builds what you want to do.
Note:
Copying a Raii object must replicate the resources it manages, so the copying behavior of the resource determines the copying behavior of the Raii object.
The general and common RAII class copying behavior is: Suppress copying, execute the reference counting method.
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Effective c++--Clause 14 (chapter 3rd)