Effective C++资源管理之(在资源管理类中小心拷贝行为)

Effective C++资源管理之(在资源管理类中小心拷贝行为)

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

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

假设我们使用C的API函数处理类型为Mutex的互斥器对象,其中有两个函数如下:

void lock(Mutex* pm);   //锁定pm所指的互斥器
void unlock(Mutex* pm); //将互斥器解锁

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

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

如果用下面的函数调用Lock对象是正确的

Mutex m;
 
void f()
{
    //管理Mutex对象
    Lock ml(&m);
    //...
 
}//函数结束后,自动调用ml的析构函数解锁

但是如果发生下面的拷贝行为,那么程序就会产生意想不到的后果:

  • 因为m2拷贝时会调用lock函数对Mutex对象进行加锁,但是m对象此时处于使用状态,因为其会阻塞等待,等待m1释放互斥量,但是m1此时也无法向下执行,因此这个程序就产生死锁了
  • 所以对于我们设计的Lock这个类,我们不希望其有拷贝行为
Mutex m;
 
void f()
{
    Lock m1(&m);//管理Mutex对象
    //...
    Lock m2(m1); //拷贝
 
}//函数结束后,自动调用ml的析构函数解锁
二、当RAII对象被拷贝时的做法

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

对底层资源做出“引用计数法”:我们可以参考shared_ptr类那样的机制,对资源设计“引用计数法”

如果我们希望对管理的资源进行引用计数法,那么可以重新设计上面的Lock类:

  1. 使用shared_ptr管理Mutex对象
  2. 当引用计数为0时,我们希望做的是解锁资源而不是删除资源,但是我们的shared_ptr提供一个“删除器”,删除器可以是一个函数或函数对象,因此下面我们为mutexPtr提供了unlokc函数,当引用计数为0时就会调用unlock函数,将Mutex对象解锁
  3. 不再设计析构函数:因为默认的析构函数会调用其非静态成员的析构函数,因此默认的析构函数会在互斥器的引用计数为0时自动调用shared_ptr的删除器(此处为unlock函数)
class Lock
{
public:
    explicit Lock(Mutex* pm) 
        :mutexPtr(pm,unlock) //提供unlock函数为删除器
    {
        lock(mutexPtr.get()); 
    }
private:
    std::shared_ptr<Mutex> mutexPtr;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值