之前条款13中说到,用对象来管理资源,然而并非所有资源都是基于堆,对于这种资源,智能指针往往不适合作为资源掌管者。
这个时候,就需要建立自己的资源管理类。
假如我们使用C API函数处理类型为Mutex的互斥对象,共有lock和unlock两个函数可用:
void lock(Mutex * pm);
void unlock(Mutex * pm);
为了确保不会忘记将一个锁住的Mutex解锁,你可能会希望建立一个类来管理。这样的类基本和条款13说的差不多,资源在构造期间获得,在析构期间释放:
class lock
{
public:
explicit lock(Mutex * pm) : mutexPtr(pm)
{lock(mutexPtr);}
~lock(){unlock(mutexPtr);}//释放资源
private:
Mutex * mutexPtr;
}
这看上去没问题,但如果Lock对象被复制会发生什么事情?
lock m1(&m);
lock m2(m1);
当一个RAII对象被复制,会发生什么事情?大多数时候你可能会选择以下两种可能:
1.禁止复制,使用条款6的做法。
2.对底层资源使用引用计数法,比如使用shared_prt,这种情况下复制RAII对象时,资源的被引用数递增.
然而如果你使用了shared_prt,它在引用次数为0的时候删除其管理的资源。那不是我们想要的行为,我们想要做的释放动作是解除锁定而不是删除。
幸好的是shared_prt允许指定删除器,当引用次数为0时删除器便被调用。
代码可以这样写:
class lock
{
public:
explicit lock(Mutex * pm) : mutexPtr(pm, unlock)
{lock(mutexPtr.get());}
~lock(){unlock(mutexPtr);}//释放资源
private:
std::shared_prt<Mutex>mutexPtr;
}
注意:lock 类不在声明析构函数,类的析构函数会自动调用非静态成员变量的析构函数,而mutexPtr会在互斥器的引用次数为0时自动调用shared_ptr的删除器。
总结:
1.复制RAII对象必须一并复制它所管理的资源。
2.普遍而场景的RAII class copying行为是:抑制Copying、使用引用计数法。