条款14:在资源管理类中小心拷贝行为(Think carefully about copying behavior in resource-managing classes.)

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

1.1 提出问题

你可能会发现,有时候需要创建自己的资源管理类。例如,假设你正在使用一个C API来操作互斥对象,互斥类型提供了lock和unlock函数:

void lock(Mutex* pm); 	// 锁住pm指向的互斥量
void unlock(Mutex* pm); 	// 互斥量解锁
class Lock {
public:
    explicit Lock(Mutex* pm)
        : mutexPtr(pm)
    {
        lock(mutexPtr);
    } // 获取资源
    ~Lock() { unlock(mutexPtr); } // 释放资源
private:
    Mutex* mutexPtr;
};
Mutex m; // 定义你需要使用的互斥量
...
{ // 创建一个区块
    Lock ml(&m); // 锁住互斥量
    ... // 执行操作
} // 离开块时,自动解锁

目前都没什么问题,但如果复制了一个Lock对象,会发生什么呢?

Lock ml1(&m); 		// 锁住 m
Lock ml2(ml1); 	// 将ml1复制到ml2,这里会发生什么?

1.2 解决办法

复制RAII对象时应该如何处理?大多数时候,你有如下选择:

  1. 禁止复制:如果允许复制RAII对象没有意义,这时应该禁止它。对于像Lock这样的类来说,这可能是正确的。
  2. 引用计数底层资源:保留资源,直到使用它的最后一个对象被销毁。std::shared_ptr会调用delete,而我们的Lock类,需要的是解锁。幸运的是,shared_ptr允许指定“删除器”。
class Lock {
//注意,Lock类,无需声明析构函数
public:
    explicit Lock(Mutex* pm)   // 用互斥量和unlock函数初始化shared_ptr
        : mutexPtr(pm, unlock) // 作为删除器
    { 
        lock(mutexPtr.get()); // 获取普通指针
    }
private:
    std::shared_ptr<Mutex> mutexPtr; // 使用shared_ptr代替普通指针
}; 
  1. 复制底部资源:有时可以有任意多个资源的副本,需要资源管理类的唯一原因是确保每个副本都能被正确释放。在这种情况下,复制资源管理对象也应该复制它包裹的资源。也就是说,复制资源管理对象时进行“深度拷贝”。例如,复制一个包含指针的对象时,不能只复制指针,还要复制指针所指向的数据。
  2. 转移底部资源的所有权:在极少数情况下,需要确保只有一个RAII对象管理资源,当复制RAII对象时,需要转移资源的所有权。

1.3 总结

  1. 复制RAII对象需要复制它管理的资源,因此资源的复制行为决定了RAII对象的复制行为。
  2. 常见的RAII类复制行为不允许复制和执行引用计数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值