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
4class 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
18class 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 天目中云