Effective C++条款14:在资源管理类中小心拷贝行为

前言

  • 在前面的文章中我们介绍了智能指针类来管理资源,但是对于某些资源,它们不是动态分配的(不是堆上的),因此智能指针就是不适合这种资源的管理了,因此你可能会设计自己的资源管理类

一、“不希望拷贝”演示案例

  • 假设我们使用C的API函数处理类型为Mutex的互斥器对象,其中有两个函数如下:
 
  1. void lock(Mutex* pm); //锁定pm所指的互斥器

  2. void unlock(Mutex* pm); //将互斥器解锁

  • 现在我们设计一个类,来管理Mutex对象,并且使用了上一篇文章的RAII规则,即“在构造期间获得资源,在析构期间释放资源”,原型如下:
 
  1. class Lock

  2. {

  3. public:

  4. //获得资源

  5. explicit Lock(Mutex* pm) :mutexPtr(pm) {

  6. lock(mutexPtr);

  7. }

  8.  
  9. //释放资源

  10. ~Lock() { unlock(mutexPtr); }

  11. private:

  12. Mutex *mutexPtr;

  13. };

  • 如果用下面的函数调用Lock对象是正确的
 
  1. Mutex m;

  2.  
  3. void f()

  4. {

  5. //管理Mutex对象

  6. Lock ml(&m);

  7. //...

  8.  
  9. }//函数结束后,自动调用ml的析构函数解锁

  • 但是如果发生下面的拷贝行为,那么程序就会产生意想不到的后果:
    • 因为m2拷贝时会调用lock函数对Mutex对象进行加锁,但是m对象此时处于使用状态,因为其会阻塞等待,等待m1释放互斥量,但是m1此时也无法向下执行,因此这个程序就产生死锁了
    • 所以对于我们设计的Lock这个类,我们不希望其有拷贝行为
 
  1. Mutex m;

  2.  
  3. void f()

  4. {

  5. Lock m1(&m);//管理Mutex对象

  6. //...

  7.  
  8. Lock m2(m1); //拷贝

  9.  
  10. }//函数结束后,自动调用ml的析构函数解锁

二、当RAII对象被拷贝时的做法

  • 当RAII对象被复制时,我们通常有以下两种做法:
    • 禁止复制:许多时候,RAII对象被复制时不合理的,就像上面我们那个Mutex一样。因此我们可以根据条款6的做法,将拷贝操作声明为private或者让其继承于Uncopyable这样的基类
    • 对底层资源做出“引用计数法”:我们可以参考shared_ptr类那样的机制,对资源设计“引用计数法”

四、引用计数法

  • 如果我们希望对管理的资源进行引用计数法,那么可以重新设计上面的Lock类:
    • 使用shared_ptr管理Mutex对象
    • 当引用计数为0时,我们希望做的是解锁资源而不是删除资源,但是我们的shared_ptr提供一个“删除器”,删除器可以是一个函数或函数对象,因此下面我们为mutexPtr提供了unlokc函数,当引用计数为0时就会调用unlock函数,将Mutex对象解锁
    • 不再设计析构函数:因为默认的析构函数会调用其非静态成员的析构函数,因此默认的析构函数会在互斥器的引用计数为0时自动调用shared_ptr的删除器(此处为unlock函数)
 
  1. class Lock

  2. {

  3. public:

  4. explicit Lock(Mutex* pm)

  5. :mutexPtr(pm,unlock) //提供unlock函数为删除器

  6. {

  7. lock(mutexPtr.get()); //条款15会介绍get函数

  8. }

  9. private:

  10. std::shared_ptr<Mutex> mutexPtr;

  11. };

五、总结

  • 赋值RAII对象必须一并复制它所管理的资源,所以资源的拷贝行为决定RAII对象的拷贝行为

  • 普遍而常见的RAII类拷贝行为是:抑制拷贝、试行引用计数法。不过其他行为也都可能被实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值