Effective C++ 条款14 在资源管理类中小心 copying 行为
条款14 : 在资源管理类中小心 copying 行为
本条款是在我们自己建立资源管理类时要注意的行为, 但是归根结底, 我们为什么要自己建立资源管理类呢 ? 为什么不用 shared_ptr ? 这是我们在本条款中需要首先解决迭代问题.
书中提出, C++提供的智能指针是适配于heap-based资源上的, 其要求不管是自动生成还是手动完成, 该资源必须要有析构函数, 然而并非所有的资源都是heap-based的, 简单来说就是有些资源没有对应的析构函数, 而是选择了别的方式进行资源的释放, 非常典型的就是文件句柄, 其必须要调用close()函数释放, 你如果直接把其交给shared_ptr而不做其他动作, 可以确定的是shared_ptr并不会智能到把close()加到智能函数中, 文件句柄不会被释放, 这样的资源还有很多, 而且大部分都很关键, 比如锁, 数据库连接, 网络socket等. 因此我们需要自己建立自己的资源管理类(当然也有些其他的方式).
怎么建立自己的资源管理类?
简单来说还是遵循RAII原则, 构造即初始化, 析构即释放资源, 这里的释放资源具体到文件就是调用close(), 具体到锁就是调用unlock(), 我们自己应当考量, 而我们一般称其为 RAII风格的XXX .
书中给出了一段代码, 用于实现RAII风格的锁 :
| 1 |  | 
于是我们就可以实现以下代码 :
| 1 |  | 
言归正传, 当一个RAII对象被复制, 会发生什么事 ?
以下是可能发生的复制策略 :
- 禁止复制 : 很多时候我们并不希望资源管理类可以复制, 就像锁, 我们一定不希望多个锁对象控制同一个底层的互斥器, 这有违锁设计的初衷, 所以直接禁止就好了, 这时我们的 - Uncopyable类就有用了 :- 1 
 2
 3
 4- class Lock: private Uncopyable { // 直接private继承自Uncopyable
 public:
 ... // 同上
 };
- 对底层资源祭出”引用计数法” : 这个其实就是利用 - shared_ptr实现资源共享就好了.- 需要注意的就是, - shared_ptr还有一个和共享内存无关的性质—删除器, 这是一个函数, 可以传入- shared_ptr构造函数的第二参数, 如果没有删除器会默认调用析构, 有删除器就调用删除器, 这其实就在一定程度上解决了智能指针只能针对- heap-based资源的问题, 让没有析构函数的资源也可以通过调用删除器实现释放, 以下是书中的代码 :- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18- class Lock {
 public:
 explicit Lock(Mutex *pm)
 : mutexPtr(pm, unlock) // 初始化智能指针, 将unlock设置为删除器
 lock(mutexPtr.get());
 }
 
 void unlock(Mutex* mtx) {
 if (mtx) {
 mtx->unlock();
 }
 
 // 不需要写析构函数了, 它可以被删除器替代
 }
 
 private:
 std::tr1::shared_ptr<Mutex> mutexPtr; // 使用 shared_ptr
 };- 请注意如果我们写的资源管理类不希望共享资源, - shared_ptr可以共享就带来了隐患, 像是上面的锁, 其实更推荐禁止拷贝的做法, 这种做法只是告诉我们一种其他的做法而已.
- 复制底层资源 : 这其实就是我们常说的深拷贝. 
- 转移底部资源的拥有权 : 为了保证资源的独占性, 我们可以选择这种策略. 
请记住 :
- 复制RAII对象必须一并复制他所管理的资源, 资源的copying行为决定RAII对象的copying行为.
- 普遍的copying行为是禁止复制或施行引用计数法.
- 文件句柄和锁这类资源可以选择禁止复制, 支持移动的策略
by 天目中云