Effective C ++, 3rd edition, item 14: carefully consider the copy behavior in resource-management classes (resource management class)

Source: Internet
Author: User

Item 14: carefully consider the copy behavior in resource-management classes (resource management class)

By Scott Meyers

Translator: fatalerror99 (itepub's nirvana)

Release: http://blog.csdn.net/fatalerror99/

Item 13 describesResource Acquisition is initialization(RaiI) principles, and describes how auto_ptr and tr1: shared_ptr use this principle in heap-based (heap-based) resources. However, not all resources are heap-based. For such resources, smart pointers (smart pointers) such as auto_ptr and tr1: shared_ptr) it is generally not as appropriate as resource handlers. In this case, you may need to create your own resource-management classes based on your own needs ).

For example, assume that you use the lock and unlock functions provided by a c api to manipulate mutex objects (mutex objects) of the mutex type ):

Void lock (mutex * PM); // lock mutex pointed to by PM

Void unlock (mutex * PM); // unlock the mutex

To ensure that you never forget to unlock a mutex lock, you plan to create a class to manage the lock. The basic structure of such a class is stipulated by the raiI principle, and resources are obtained through construction (construction) and released through destruction (destructor:

Class lock {
Public:
Explicit lock (mutex * pm)
: Mutexptr (pm)
{Lock (mutexptr);} // acquire resource

~ Lock () {unlock (mutexptr);} // release resource

PRIVATE:
Mutex * mutexptr;
};

The customer uses lock in the usual raiI style:

Mutex m; // define the mutex you need to use
...
{// Create block to define critical section
 LockML (& 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 the ml1 to ml2-what shocould
// Happen here?

This is a specific example of a more general problem. Every raiI class author must face it: what should happen when a raiI object is copied? In most cases, you need to select one of the following possibilities:

  • Prohibit copying(Copy prohibited ). In many cases, it is meaningless to allow raiI objects to be copied. This is probably true for a class like lock, because synchronizing the original "copy" has little significance. When copying a file is meaningless to a raiI class, you should disable it. Item 6 explains how to do this: declare that the copy operation is private. For lock, it may look like this:

Class lock:Private uncopyable{// Prohibit copying-see
Public: // Item 6
... // As before
};

  • Reference-count the underlying resource(Reference count of underlying resources ). Sometimes what people need is to keep a resource until the last object that uses it is destroyed. In this case, copying a raiI object should increase the number of objects that reference this resource. This is the meaning of "copy" ("copy") used by tr1: shared_ptr.

Generally, raiI classes only needs to contain a tr1: shared_ptr data member (data member) to copy the reference-counting (reference count. For example, if the lock uses reference counting, it may change the mutexptr type from mutex * To tr1: shared_ptr <mutex>. Unfortunately, the default behavior of tr1: shared_ptr is to delete it when the reference count (reference count) of the things it points to changes to zero, but this is not what we want. After we use a mutex, we want to unlock it instead of deleting it.

Fortunately, tr1: shared_ptr allows a function (function) or function object (function object) called when the reference count (reference count) Changes to zero )). (This function is not available in auto_ptr. auto_ptr always deletes its pointer (pointer ).) Deleter is the optional second parameter of Constructor (constructor) of tr1: shared_ptr. Therefore, the code looks like this:

Class lock {
Public:
Explicit lock (mutex * pm) // init shared_ptr with the mutex
: Mutexptr (PM,Unlock) // To point to and the unlock func
{// As the deleter

Lock (mutexptr.Get ()); // See item 15 for info on "get"
}
PRIVATE:
STD: tr1: shared_ptr <mutex>Mutexptr; // use shared_ptr
}; // Instead of raw pointer

In this example, note how the lock class does not declare a destructor (destructor. That's because it does not need. Item 5 explains the Destructor (destructor) of a class (whether it is compiler-generated (generated by the compiler) or user-defined) the Destructors (destructor) of the non-static data members (non-static data member) of this class is automatically called ). In this example, mutexptr is used. However, when the reference count (reference count) of mutex changes to zero, the Destructor (destructor) of mutexptr automatically calls the deleter of tr1: shared_ptr -- Unlock here. (Most people who have read the source code of this class realize that they need to add a comment to indicate that you do not forget the destruction (analysis structure), but only rely on the default behavior of compiler-generated (generated by the compiler .)

  • Copy the underlying resource(Copy underlying resources ). Sometimes, as you want, you can have multiple copies of a resource. You need a resource-management class) the only reason is to make sure that it is released after each copy is used. In this case, copying a resource-management object should also copy the resources it encapsulates. That is to say, to copy a resource-management object, you need to perform a "Deep copy" ("Deep copy ").

Some standard string types are implemented by pointers to heap memory (pointer to heap memory), where the string (string) characters are stored. Such strings objects contains a pointers to heap memory (pointer to heap memory ). When a String object is copied, a copy should consist of the pointer and the memory to which it points. This strings is represented by deep copying (deep copy ).

  • Transfer ownership of the underlying resource(Transfer ownership of underlying resources ). In special cases, you may want to ensure that only one raiI object references one raw Resource (raw resource). When this raiI object is copied, the ownership of the resource is transferred from the Copied object to the copying object ). As described in item 13, this is the meaning of "copy" ("copy") used by auto_ptr.

Copying functions (copy function) (copy constructor) and copy assignment operator (copy assignment operator) may be generated by the compiler, unless compiler-generated (generated by the compiler) the version is exactly what you want (Item 5 illustrates these default actions), otherwise you should write them yourself. In some cases, you need to support the generic versions of these functions. This version is described in item 45.

Things to remember

  • To copy a raiI object, you must copy the resources it manages. Therefore, the copying behavior of the resource determines the copying behavior of the raiI object.
  • In general, the copying behaviors of raiI class are disallowing copying (copying is not allowed) and round-ming reference counting (reference counting is implemented), but other behaviors are also possible.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.