条款15 在资源管理类中提供对原始资源的访问

总结:

API 经常需要访问原始资源,所以每一个 RAII 类都应提供取得它所管理资源的方法。

访问可以通过显式转换或者隐式转换进行。通常,显式转换更安全,而隐式转换对客户来说更方便。


很多 API 直接涉及资源,所以除非你计划坚决放弃使用这样的 API(太不实际),否则,你就要经常绕过资源管理类而直接处理原始资源(raw resources)。

例如使用类似 auto_ptr 或 tr1::shared_ptr 这样的智能指针来保存 createInvestment 这样的 factory 函数的结果,并希望以某个函数处理Investment对象:

std::tr1::shared_ptr<Investment> pInv(createInvestment());

int daysHeld(const Investment *pi); // 返回投资天数

int days = daysHeld(pInv); // 错误!

daysHeld 要求一个Investment* 指针,但是你传给它一个类型为 tr1::shared_ptr<Investment> 的对象。你需要一个将 RAII 类(本例为 tr1::shared_ptr)对象转化为它所包含的原始资源(本例为底部之Investment*)的函数。有两个常规方法来做这件事:显式转换和隐式转换。

tr1::shared_ptr 和 auto_ptr 都提供一个 get 成员函数进行显示转换,也就是说,返回一个智能指针对象内部的原始指针(或它的一个副本):

int days = daysHeld(pInv.get());// 将pInv内的原始指针传给daysHeld

就像几乎所有智能指针一样,tr1::shared_ptr和 auto_ptr 也都重载了指针解引用操作符(operator-> 和 operator*),它们允许隐式转换到底部原始指针:

class Investment { // investment继承体系的根类
public: 
   bool isTaxFree() const;
   ...
};
Investment* createInvestment(); // factory函数

std::tr1::shared_ptr<Investment> pi1(createInvestment());

bool taxable1 = !(pi1->isTaxFree()); // 经由operator->访问资源
...

std::auto_ptr<Investment>pi2(createInvestment());

bool taxable2 = !((*pi2).isTaxFree()); // 经由operator*访问资源

...

再考虑以下这个用于字体的RAII类(对C CPI而言字体是一种原生数据结构):

FontHandle getFont(); // C API,为求简化略参数

void releaseFont(FontHandle fh); // 来自同一组C API
class Font { // RAII class
public:
    explicit Font(FontHandle fh): f(fh){} // 值传递获得资源

       ~Font() { releaseFont(f); }

    private:
    FontHandle f; // 原始字体资源
};

假设有大量与字体相关的C API,它们处理的是FontHandle,这就需要频繁地将 Font 对象转换为 FontHandle。Font 类可以提供一个显式的转换函数,比如get:
FontHandle get() const {return f; }

不幸的是,这就要求客户每次与 API 通信时都要调用 get:

void changeFontSize(FontHandle f, intnewSize); // C API

Font f(getFont());
int newFontSize;
...

changeFontSize(f.get(), newFontSize); // 显式地将Font转换为FontHandle

一些程序员可能发现对显式请求这个转换的需求足以令人郁闷而避免使用这个类。另一个可选择的办法是为 Font 提供一个隐式转换函数,转型为FontHandle:
operator FontHandle()const { return f; }

这样就可以使对C API的调用简单自然:

changeFontSize(f, newFontSize); // 隐式转换,与上例作对照

不利的方面是隐式转换增加错误发生机会。例如,一个客户可能会在有意使用 Font 的地方意外地产生一个 FontHandle:

Font f1(getFont());

...

FontHandle f2 = f1;

// 原意是复制一个Font对象,却反而将f1隐式转换为其底部的FontHandle然后才复制

当 f1被销毁,字体将被释放,f2则被悬挂(dangle)。

最好的设计就是坚持“使接口易于正确使用,不易被误用”。通常,类似get的一个显式转换函数是更可取的方式,因为它将意外的类型转换的机会减到最少。而有时候通过隐式类型转换将提高使用的自然性。

RAII类的存在并非为了封装什么东西而是为了确保资源释放这一特殊行为的发生。此外,一些 RAII类将实现的真正封装和底层资源的宽松封装结合在一起如tr1::shared_ptr 封装了它引用计数的全部机制,但它依然提供对它所包含资源的简单访问。就像大多数设计良好的类,它隐藏了客户不需要看到的,但它也让客户确实需要访问的东西可以被利用。

参考资源链接:[数字古籍图书下载工具bookget使用介绍](https://wenku.csdn.net/doc/npvss1h3zk?utm_source=wenku_answer2doc_content) 数字古籍图书下载工具bookget能够帮助用户从多种资源中下载古籍图书。为了使用这一工具并有效地管理下载的古籍资源,你需要遵循以下步骤和注意事项: 首先,确保你的下载行为符合版权法律和资源使用条款。通常,古籍数据库提供公共访问权或基于特定协议的访问权,确认你有权下载所需的古籍资源。 其次,下载并安装bookget工具。你可以从提供资源链接《数字古籍图书下载工具bookget使用介绍》获取最新版本的工具和安装说明。 打开bookget工具后,你可以设置下载参数,包括但不限于选择下载的古籍目录、格式(如PDF、EPUB等)、以及保存路径。一些工具还支持关键词搜索功能,帮助你更精确地找到需要的古籍资料。 在开始下载之前,请检查工具是否支持断点续传功能,这对于处理网络不稳定情况或长时间的下载任务非常有用。 启动下载后,工具显示下载进度。这可以帮助你掌握当前的下载状态,并在必要时调整设置。 下载完成后,你可以通过工具的用户界面管理下载队列,包括重命名、删除或重新下载任务。 由于古籍资源的特殊性,一些工具可能提供对古籍格式的特殊处理功能,如文本校对和格式化,确保你下载的电子版尽可能地保持原始的排版和内容。 最后,确保你的计算机系统兼容bookget工具,以便顺利运行。工具的系统兼容性信息通常可在使用介绍文档中找到。 当你完成下载后,建议对下载的古籍资源进行备份,并妥善管理。对于电子资源的管理,可以使用专门的电子图书管理软件,帮助你分、检索和阅读这些珍贵的学术资源。 掌握了这些操作技巧后,你可以更高效地利用数字古籍图书下载工具bookget,获取和管理你所需的学术资源。如果你想进一步提升你的技能或了解更多关于数字古籍和信息技术的知识,请参考《数字古籍图书下载工具bookget使用介绍》。这份资料详细介绍了bookget工具的使用方法,并提供了相关知识的深入解释,是提高你对数字古籍管理和信息技术理解的宝贵资源。 参考资源链接:[数字古籍图书下载工具bookget使用介绍](https://wenku.csdn.net/doc/npvss1h3zk?utm_source=wenku_answer2doc_content)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值