Effective C++ 笔记 —— Item 14: Think carefully about copying behavior in resource-managing classes.

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.

 

posted @ 2021-09-02 17:38  MyCPlusPlus  阅读(39)  评论(0编辑  收藏  举报