Effective c++ 条款14:在资源管理类中小心copying行为

并非所有资源都是heap-based,对那种资源而言,像auto-ptr和tr1::shared_ptr这样的智能指针往往不适合作为资源掌管者。因此,我们偶尔需要建立自己的资源管理类。
例如,假设我们使用互斥器对象Mutex,共有lock和unlock函数可用:

void lock(Mutex* pm); // 锁定pm所指的互斥锁
void unlock(Mutex* px); //将互斥锁解除锁定

为确保绝不会忘记将一个被锁住的Mutex解锁,我们可能会希望建立一个class用来管理互斥锁。这样的class的基本结构由RAII守则支配:资源在构造期间获得,在析构期间释放。

class Lock {
public:
    explicit Lock(Mutex* pm):mutexPtr(pm) {
        lock(mutexPtr);
    }
    ~Lock() {
        unlock(mutexPtr);
    }
private:
    Mutex* mutexPtr;
};

然而当一个RAII对象被复制时,会发生什么事?
大多情况下会选择以下两种方案:

1、对RAII class禁止复制行为

许多时候运行RAII对象被复制并不合理。如果复制动作对RAII class并不合理,我们便应该禁止他。条款6告诉了我们怎么做:将copying操作声明为private,或者继承一个Uncopyable class。

2、对底层资源祭出“引用计数法”(reference-count)

有时候我们希望保有资源,直到它的最后一个使用者(某对象)被销毁。这种情况下复制RAII对象时,应该将资源的“被引用数”递增。shared_ptr便是如此。
通常只要内含一个shared_ptr成员变量,RAII classes便可实现reference-counting copying行为。然而shared_ptr的缺省行为是“当引用次数为0时删除其所指物”,那不是我们所要的行为。而当我们用Mutex时,我们想做的释放动作是解除锁定而非删除。幸运的是shared_ptr允许指定所谓的“删除器(deleter)”,那是一个函数或函数对象,当引用次数为0时便被调用(此机能并不存在与auto_ptr——它总是将其指针删除)。删除器对shared_ptr构造函数而言是可有可无的第二参数,所以代码看起来像这样:

class Lock {
public:
    explicit Lock(Mutex* pm):mutexPtr(pm) {
        lock(mutexPtr.get());
    }
private:
    std::tr1::shared_ptr<Mutex> mutexPtr;
};

请注意,本例的Lock class不再声明析构函数,因为没有必要,因为析构函数(不论是编译器生成的还是用户自定的)会自动调用其non-static成员变量的析构函数,而mutexPtr的析构函数会在互斥器的引用次数为0时自动调用其删除器
注意你并没有忘记析构,你只是依赖了编译器生成的缺省行为。

3、复制底部资源

也即复制资源管理对象时,进行的是深度拷贝

4、转移底部资源的拥有权

某些罕见的场合下可能希望确保永远只有一个RAII对象指向一个未加工资源,即使RAII对象被复制依然如此。此时资源的拥有权会从被复制物转移到目标物。(如auto_ptr和unique_ptr)

猜你喜欢

转载自blog.csdn.net/unirrrrr/article/details/81181693