不仅仅动态分配的内存是资源,像文件描述器,互斥锁,数据库连接,以及网络sockets等也是资源,如果不及时关闭,也会造成资源浪费。
重点1:建立自己的资源管理类
如果是heap-based资源,则可以用auto_ptr或shared_ptr管理资源。然而并非所有资源都是heap-based,这样,就需要建立自己的资源管理类。
以互斥锁为例,为确保不会忘记将一个被锁住的mutex解锁,我们希望建立一个class来管理机锁。这样的class的基本结构由RAII守则支配,也就是“资源在构造期间获得,在析构期间释放”:
class Lock
{
public:
explicit Lock(Mutex* pm):mutexPtr(pm)
{
lock(mutexPtr);
}
~Lock()
{
unlock(mutexPtr);
}
private:
Mutex *mutexPtr;
};
客户对Lock的用法符合RAII方式:
Mutex m;
...
{
Lock m1(&m);
...
}
难点1:RAII class copying行为
如果上面Lock对象被复制,会发生什么事?
Lock m11(&m); //锁定m
Lock m12(&m11); //将m11复制到m12身上
这是一个一般化问题的特定例子:当一个RAII 对象被复制,会发生什么事?大多时候,选择下面两种可能:
1)禁止复制。
class Uncopyable
{
protected: //允许继承对象构造和析构
Uncopyable(){}
~Uncopyable(){}
private:
Uncopyable(const Uncopyable &); //但阻止copy
Uncopyable &operator=(const Uncopyable&);
};
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">为阻止Lock对象被复制,我们唯一需要做的就是继承Uncopyable:</span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name="code" class="cpp">class Lock:private Uncopyable
{
public:
...
};
2)对底层资源祭出“引用计数法”。
有时候我们希望保有资源,直到它的最后一个使用者被销毁。这个时候使用tr1::shared_ptr来引用计数。但是tr1::shared_ptr的缺省行为是“当引用次数为0时”删除其所指物,那不是我们的行为。当我们用上Mutex,我们想要做的释放动作是解锁而不是删除。
这可以给tr1::shared_ptr指定删除器来实现:
class Lock
{
public:
explicit Lock(Mutex* pm):mutexPtr(pm,unlock) //unlock为指定的删除器
{
lock(mutexPtr.get()); //get为显示转换函数
}
private:
std::tr1::shared_ptr<Mutex> mutexPtr; //使用shared_ptr代替原始指针
};
注意1:shared_ptr有两个参数,第一个是类型,第二个指定删除器,一般是函数,第二个参数可以省略,表示默认是删除对象。
注意2:本例的Lock class不再声明析构函数。class析构函数会自动调用其non-static成员变量的析构函数。