0.概述
- 复制RAII对象必须一并复制它所管理的资源,所以资源的copying行为决定RAII对象的copying行为。
- 普遍而常见的 RAII class copying行为是:抑制copying、施行引用计数法(reference counting)。不过其他行为也都可能被实现。
1.举例(mutex互斥器)
假设处理Mutex互斥器对象,共有lock和unlock两函数可用。
void lock(Mutex *pm); //锁定pm所指的互斥器
void unlock(Mutex *pm); //将互斥器解除锁定
为确保绝不会忘记将一个被锁住的 Mutex解锁,建立1个class用来管理机锁。这样的class的基本结构由RAII守则支配,也就是“资源在构造期间获得,在析构期间释放”:;
class Lock{
public:
explicit Lock(Mutex* pm):mutexPtr(pm)
{
lock(mutexPtr);//获得资源
}
~Lock(mutexPtr);//释放资源
private:
Mutex *mutexPtr;
};
对Lock的用法需要符合RAII方式:
Mutex m; //定义需要的互斥器
...
{ //建立区块定义critical section
Lock m1(&m); //锁定互斥器
... //执行critical section内的动作
}//区块末尾自动解除互斥器锁定
2.Lock对象被复制会发生什么?
但是如果Lock对象被复制会发生什么?
Lock m1(&m); //锁定m
Lock m2(m1); //会发生啥?
一般会选择一下两种可能:
2.1 禁止复制
如果复制操作对RAII class不合理,就应该禁止。参考Item6:将copying操作声明为private,以Lock为例:
class Lock:private Uncopyable{
public:
...
};
2.2 对底层资源祭出“引用计数法”(reference-count)
有时候我们希望保有资源,直到它的最后一个使用者(某对象)被销毁。这种情况下复制RAII对象时,应该将资源的“被引用数”递增。tr1::shared_ptr便是如此。通常只要含有一个tr1::shared_ptr成员变量,RAlI classes便可实现出reference-counting copying行为。如果Lock打算使用reference counting,可以将mutexPtr的类型从Mutex*改为tr1::shared_ptr<Mutex>。
然而!!tr1: :shared_ptr的缺省行为是“当引用次数为0时删除其所指物”,这不是我们想要的。当是使用Mutex时,我们想要做的释放动作是解除锁定而非删除。
但是tr1::shared_ptr允许指定“删除器”(deleter),这是一个当引用次数为0时会被调用的函数或函数对象(注:auto_ptr中不存在这种机制,它总是将指针删除)。删除器对于tr1::shared_ptr构造函数是一个可选的第二参数,代码如下:
class Lock{
public:
explicit Lock(Mutex* pm)//以某个Mutex初始化shared_ptr
:mutexPtr(pm,unlock)//以unlock为删除器
{
lock(mutexPtr.get());
}
private:
std::tr1::shared_ptr<Mutex> mutexPtr;//使用shared_ptr替换raw pointer
};
注:不需要再声明析构函数。Item5:class析构函数(无论是编译器生成的,或用户自定的)会自动调用其non-static成员变量(本例为mutexPtr)的析构函数。而mutexPtr的析构函数会在互斥器的引用次数为0时自动调用tr1::shared_ptr的删除器(unlock)。
2.3 复制底部资源
有时候可以针对一份资源拥有其任意数量的复件(副本)。而需要“资源管理类”的唯一理由是,当你不再需要某个复件时确保它被释放。在此情况下复制资源管理对象,应该同时也复制其所包覆的资源。也就是说,复制资源管理对象时,进行的是“深度拷贝”。
某些标准字符串类型是由“指向heap内存”的指针构成(那内存被用来存放字符串的组成字符)。这种字符串对象内含一个指针指向一块heap 内存。当这样一个字符串对象被复制,不论指针或其所指内存都会被制作出一个复件。这样的字符串展现深度复制(deep copying〉行为。
2.4 转移底部资源的拥有权
某些罕见场合下可能希望确保永远只有一个RAII对象指向一个未加工资源(raw resource),即使RAII对象被复制依然如此。此时资源的拥有权会从被复制物转移到目标物。Item13:这是auto_ptr奉行的复制意义。