留心资源管理类中的复制行为
来源:互联网 发布:电脑淘宝怎么货到付款 编辑:程序博客网 时间:2024/04/30 09:30
“资源获取即初始化”( Resource Acquisition Is Initialization ,简称 RAII )是资源管理的主要内容。使用 auto_ptr和tr1::shared_ptr描述了如何管理堆上的资源的。然而并不是所有的资源都分配于堆上,对于不分配于堆上的资源,类似于auto_ptr和tr1::shared_ptr这一类的智能指针并不适合于处理它们。这是千真万确的,必须不时地自己动手,创建自己的资源管理类。
举例说,我们使用一个 C 语言的 API 所提供 的lock和unlock函 数 来处理Mutex类型的互斥对象:
void lock(Mutex *pm); // 通过 pm 为互斥量上锁
void unlock(Mutex *pm); // 为互斥量解锁
为了确保已经上锁的互斥量都得到解锁,应该自己编写一个类来管理互斥锁。这样的类的基本结构应遵 循 RAII 的 原理,那就是:资源在构造过程中获得,在析构过程中释放:
class Lock {
public:
explicit Lock(Mutex *pm)
: mutexPtr(pm)
{ lock(mutexPtr); } // 获取资源
~Lock() { unlock(mutexPtr); } // 释放资源
private:
Mutex *mutexPtr;
};
客户端程序员通过传统 的 RAII 风 格来使用Lock类:
Mutex m; // 定义互斥量以便使用
...
{ // 创建程序块用来定义临界区
Lock ml(&m); // 为互斥量上锁
... // 进行临界区操作
} // 在程序块末尾互斥量将自动解锁
这样可以正常工作,但是如果复制一个Lock对象,将会发生些什么呢?
Lock ml1(&m); // 为 m 上锁
Lock ml2(ml1); // 把 ml1 复制给 ml2
// 将会发生什么呢?
有一个问题是所有 的 RAII 类创建者必须面对的,那就是:当复制一个 RAII 对象时需要做些什么呢?以上是对于这个一般化问题的一个较具体的示例。大多数时候,以下四种可行的方案供选择。
l 禁止复制。 在许多情况下,允许 RAII 被复制没有任何意义。比如对于 Lock 类来说就是这样,因为复制同步原型在大多数情况下都没有什么意义。当复制一个 RAII 类无意义时,就应该禁止它。将拷贝赋值运算符声明为私有的。对于 Lock 而言,应该是下面的情形:
class Uncopyable {
protected: // 允许派生类存在构造器和析构器
Uncopyable() {}
~Uncopyable() {}
private:
Uncopyable(const Uncopyable&); // 但禁止复制
Uncopyable& operator=(const Uncopyable&);
};
class Lock: private Uncopyable { // 防止复制
public:
... // 同上
};
l 为基础资源进行引用计数。 有时,我们期望能保留对一个资源的所有权,直到其所涉及的最后一个对象被删除为止。在这种情况下,复制一个 RAII 对象将会添加一个引用资源对象的计数。这就是 tr1::shared_ptr 所使用的“复制”的含义。
通常情况下, RAII 类可以通过包含一个 tr1::shared_ptr数据成员来实现引用计数复制行为。举例说,如果Lock曾希望使用引用计数,它可能会将mutexPtr的类型从Mutex*更改为tr1::shared_ptr<Mutex>。但是不幸的是,tr1::shared_ptr默认的行为是:当引用计数值变为零时,删除其所指向的内容,但这不是我们想要的。当一个Mutex用完时,我们希望对其进行的操作是解锁,而不是删除它。
所幸的是,tr1::shared_ptr允许定义一个“删除器”,它是一个函数或一个函数对象,在引用计数值为零时,它将得到调用。(auto_ptr并不包含这一特性,它总是删除它所指向的内容。)删除器可作为tr1::shared_ptr构造函数的另一个可选参数,所以代码应该是这样的:
class Lock {
public:
explicit Lock(Mutex *pm) // 初始化 shared_ptr ,参数为
: mutexPtr(pm, unlock) // 所指向的互斥量和解锁函数
lock(mutexPtr.get()); //
}
private:
std::tr1::shared_ptr<Mutex> mutexPtr;
}; // 使用 shared_ptr 而不是裸指针
在本示例中,请注意 Lock 类不再声明析构函数。这是因为我们不再需要它了。第 5 条中介绍了一个类的析构函数(无论是编译器自动生成的还是用户自定义的)会自动为类的非静态数据成员进行析构。本示例中 mutexPtr 就将被自动析构。 当互斥量的引用计数变为零时, 析构函数会析构 mutexPtr,然而此时实际上 将会调用 tr1::shared_ptr的删除器unlock。(通常应该为这个类的代码添加一段注释,告诉大家我们并没有忘记编写析构函数,而是把工作留给了编译器自动生成的默认析构函数。)
l 复制主要的资源。 一些时候,你可以在需要的情况下为资源复制出任意份数的副本,此时你需要一个资源管理类的唯一理由就是:确保每份副本在其工作完成之后得到释放。在这种情况下,复制资源管理对象的同时,也要复制出其涉及的资源。也可以说,复制一个资源管理对象时,将进行“深度复制”。
标准 string 类型的一些实现版本中,包含着一个指向堆内存的指针,这个指针所指向的就是字符串所保存的位置。这样的 string 对象包含着一个指向堆内存的指针。当一个 string 对象被复制完成之后,复制出的这一副本将由这一指针和其指向的内存共同组成。这样的 string 就进行了一次深度复制。
l 传递主要资源的所有权。 在少数情况下,可能需要确保仅仅有一个 RAII 对象引用了一个未定义类型的资源,当复制这一 RAII 对象时,资源的所有权也从源对象传递到目标对象了。如同 auto_ptr 所实现的“复制”的含义。
拷贝函数(拷贝构造函数和拷贝赋值运算符)可能由编译器自动生成,但是如果编译器自动生成版本无法满足我们的需要,就应该自己编写这些函数。在一些情况下,你能还会需要这些函数的范型版本
牢记在心
l 复制一个 RAII 对象的同时也要复制其所管理的对象,所以资源管理的复制行为由 RAII 对象的复制行为决定。
l 一般的 RAII 类在复制时应遵循两条原则:不允许使用复制,要进行资源计数。但也不要拘泥于这两个原则。
- 留心资源管理类中的复制行为
- 第14条:留心资源管理类中的复制行为
- 精通C++资源管理-在资源管理类中小心coping行为
- [翻译] Effective C++, 3rd Edition, Item 14: 谨慎考虑 resource-managing classes(资源管理类)中的拷贝行为
- 条款14: 在资源管理类中小心copying行为
- 条款14、 在资源管理类中小心copying行为
- 条款14: 在资源管理类中小心copying行为
- C++箴言:谨慎考虑资源管理类的拷贝行为
- Effective C++(14) 在资源管理类中小心copying行为
- item14: 在资源管理类中心小copying行为
- 条款14:在资源管理类中小心coping行为
- 条款14:在资源管理类中小心copying行为
- Effective C++:条款14:在资源管理类中小copying行为
- 条款14 在资源管理类中小心copying行为
- 条款14 在资源管理类中小心copying行为
- 条款14:在资源管理类中小心coping行为
- 条款14 在资源管理类中小心coping 行为
- effective c++ 在资源管理类中小心copying 行为
- SVC笔记之一
- vista下iis7.0的设置
- 字符数字随机生成源码
- 如此美丽
- Windows扫雷模拟程序(源码)
- 留心资源管理类中的复制行为
- sping web mvc快速入门 三篇
- C/C++程序员求职面试
- SQL Server补丁安装常见问题(更新)
- 反思
- 获取dataGridView当前行的值
- .net中“检测到有潜在危险的 Request.Form 值”错误解决方法
- 2007最牛X网站收集[转]
- 生活充满着无奈