侧边栏壁纸
博主头像
如此肤浅

但行好事,莫问前程!

  • 累计撰写 52 篇文章
  • 累计创建 6 个标签
  • 累计收到 6 条评论

目 录CONTENT

文章目录

条款14 在资源管理类中小心coping行为

如此肤浅
2022-05-17 / 0 评论 / 0 点赞 / 37 阅读 / 1,773 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-05-19,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1. 复制 RAII 对象必须一并复制它所管理的资源,所以资源的 copying 行为决定 RAII 对象的 copying 行为。
2. 普遍而常见的 RAII class copying 行为是:抑制copying、施行引用计数法(reference counting)。不过其他行为也都可能被实现。

条款13中auto_ptrtr1::shared_ptr这样的智能指针不适用于不是 heap-based 的资源管理!

问题描述

例如,假设我们使用 C API 函数处理类型为 Mutex 的互斥锁对象,共有 lock 和 unlock 两函数可用:

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

为确保绝不会忘记将一个被锁住的 Mutex 解锁,你可能会希望建立一个 class 用来管理机锁。这样的 class 的基本结构由 RAII 守则支配,也就是“资源在构造期间获得,在析构期间释放”:

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

客户对 Lock 的用法符合 RAII 方式:

Mutex m; //定义你需要的互斥锁
...
// 建立一个区块来定义关键的部分
{ 
Lock ml(&m); // 锁定互斥锁
	... // 执行关键部分内的操作
       // 在区块最末尾,自动解除互斥锁的锁定
}

这样操作很好,但如果 Lock 对象被复制,会发生什么呢?

Lock ml1(&m); // 锁定
Lock ml2(ml1); //将ml1复制到ml3上,会发生什么?

解决方式

禁止复制

许多时候允许 RAII 对象被复制并不合理,因此将 coping 操作声明为 private 可以禁止复制(条款6):

class Lock: private Uncopyable { // 禁止复制,见条款6
public:
	... // 和前面一样
};

对底层资源祭出“引用计数法”(reference-count)

对底层资源祭出“引用计数法”(reference-count)。有时候我们希望保有资源,直到它的最后一个使用者(某对象)被销毁。这种情况下复制 RAII 对象时,应该将资源的“被引用数”递增。tr1::shared_ptr 便是如此。

通常只要内含一个 tr1::shared_ ptr 成员变量,RAII classes 便可实现出 reference-counting copying 行为。如果前述的 Lock 打算使用 reference counting,它可以改变 mutexPtr 的类型,将它从 Mutex* 改为 tr1::shared_ ptr<Mutex>。然而很不幸 tr1::shared_ptr 的缺省行为是“当引用次数为0时删除其所指物”,那不是我们所要的行为。当我们用上一个 Mutex,我们想要做的释放动作是解除锁定而非删除。

幸运的是 tr1::shared_ptr 允许指定所谓的“删除器”(deleter),那是一个函数或函数对象(function object),当引用次数为0时便被调用(此机能并不存在于auto_ptr——它总是将其指针删除)。删除器对 tr1::shared_ptr 构诰函数而言是可有可无的第二参数,所以代码看起来像这样:

class Lock {
public:
	// 以某个Mutex初始化shared_ptr,
    // 并以unlock函数为删除其。
	explicit Lock(Mutex* pm): mutexPtr(pm, unlock)
    {
    	lock(mutexPtr.get()); // 条款15提到“get”
    }
private:
	// 使用shared_ptr替换原始的指针
	std::tr1::shared_ptr<Mutex> mutexPtr;
};

请注意,本例的 Lock class 不再声明析构函数。因为没有必要。条款5说过,class 析构函数(无论是编译器生成的,或用户自定的)会自动调用其 non-static 成员变量(本例为mutexPtr))的析构函数。而 mutexPtr 的析构函数会在互斥器的引用次数为0时自动调用 tr1::shared_ptr 的删除器(本例为 unlock)。

0

评论区