您的位置:首页 > 编程语言 > C语言/C++

Effective C++(14) 在资源管理类中小心copying行为

2014-01-23 09:29 316 查看
问题聚焦:
上一条款所告诉我们的智能指针,只适合与在堆中的资源,而并非所有资源都是在堆中的。
这时候,我们可能需要建立自己的资源管理类,那么建立自己的资源管理类时,需要注意什么呢?。

在详述这一章的主题之前,先回忆一下上一节所提到的一个名词——RAII(Resource Acquisition Is Initialization)
含义就是:资源取得时机便是初始化时机。
如果上一节对这个观念的理解还不是很深的话,那么下面这个例子可以让你更好地理解。

Demo 假设我们使用C API函数处理类型为Mutex的互斥器对象,共有lock和unlock两函数可用。
void lock(Mutex* pm);
void unlock(Mutex* pm);


为了确保不会忘记将一个被锁住的Mutex解锁,你可能会希望简历一个class用来管理锁。
这样的class的基本结构由RAII守则支配,也就是在“资源构造期间获得,在析构期间释放”
class Lock {
public:
explicit Lock(Mutex* pm) : mutexPtr(pm)
{ lock(mutexPtr);    }     // 获得资源
~Lock()
{ unlock(mutexPtr); }    // 释放资源
};

// 客户对Lock的正确用法符合RAII方式
Mutex m;         // 定义你需要的互斥器
....
{                         // 建立一个区块用来定义critical section
Lock ml(&m);      // 锁定互斥器
......
}                        // 在区块最末尾,自动接触互斥器锁定


上面的用法自然没有什么问题,那么问题是什么呢?——如果Lock对象被复制,会发生什么事呢?就像下面这样:
Lock ml1(&m);
Lock ml2(ml1);


一般化这个问题就是:当一个RAII对象被复制时, 会发生什么事情呢?

大多数情况下,有两种解决方法:
1 禁止复制:复制动作对RAII class并不合理。如果阻止复制操作,可以转到:Effective
C++(6) 如何拒绝编译器的自动生成函数
2 对底层资源祭出“引用计数法:
通常,只要内含一个tr::shared_ptr成员变量,就可以实现引用技术。代码是下面这个样子的:
class Lock {
public:
explicit Lock(Mutex* pm) : mutexPtr(pm, unlock)
{
lock(mutexPtr.get());
}
private:
std::tr1LLshared_ptr<Mutex> mutexPtr;
};


需要注意的一点是:tr1::shared_ptr的缺省行为是“当引用次数为0时,删除其所指对象”,或许这并不是我们想要的行为。幸运的是,tr1::shared_ptr并不是只能删除对象,而是允许指定我们想要的动作,只需要在第二个参数上传递一个对象或函数对象,当引用次数为0时,便被调用。
在本例中,传递的就是解锁函数,而不是删除所指对象。

遇到RAII的复制动作时,我们还可以有别的处理方式:
1 复制底部资源
复制资源管理对象时,同时也可以复制其所包含的资源,因此进行的应该是“深拷贝”。
例如:当一个对象包含一个指针指向一块堆内存时,复制这个对象,同时其指针和指向的内存都会被复制出一个复件。
2 转移底部资源的拥有权
在某些场合下,你可能希望确保永远只有一个RAII对象指向一个未加工的资源,就像auto_ptr的复制行为。详见: Effective
C++(13) 用对象管理资源

小结:

复制RAII对象必须一并复制它所管理的资源,所以资源的拷贝行为决定RAII对象的拷贝行为
一般的复制行为是:阻止拷贝行为,使用引用计数法(tr1::shared_ptr)

参考资料:
《Effective C++ 3rd》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: