[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风格的锁 :

class Lock {
public:
  explicit Lock(Mutex *pm)
  : mutexPtr(pm)
  { lock(mutexPtr); }                          // 获得资源

  ~Lock() { unlock(mutexPtr); }                // 释放资源

private:
  Mutex *mutexPtr;
};

于是我们就可以实现以下代码 :

Mutex m;                    // 设置一个互斥器
...
{                           // 这是一块作用域, 可以是一个要求线程安全的函数内部
	Lock ml(&m);            // 直接用互斥器上锁
	...                     // 执行对锁有需求的行为
}    						// 离开作用域自动调用析构, 析构中自动unlock

言归正传, 当一个RAII对象被复制, 会发生什么事 ?

以下是可能发生的复制策略 :

  • 禁止复制 : 很多时候我们并不希望资源管理类可以复制, 就像锁, 我们一定不希望多个锁对象控制同一个底层的互斥器, 这有违锁设计的初衷, 所以直接禁止就好了, 这时我们的Uncopyable类就有用了 :

    class Lock: private Uncopyable {        // 直接private继承自Uncopyable
    public:                                    
    ...                                     // 同上   
    };
    
  • 对底层资源祭出"引用计数法" : 这个其实就是利用shared_ptr实现资源共享就好了.

    需要注意的就是, shared_ptr还有一个和共享内存无关的性质—删除器, 这是一个函数, 可以传入shared_ptr构造函数的第二参数, 如果没有删除器会默认调用析构, 有删除器就调用删除器, 这其实就在一定程度上解决了智能指针只能针对heap-based资源的问题, 让没有析构函数的资源也可以通过调用删除器实现释放, 以下是书中的代码 :

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值