1.为什么需要访问资源管理类中的原生资源 

资源管理类是很奇妙的。它们是防止资源泄漏的堡垒,没有资源泄漏发生是设计良好的系统的一个基本特征。在一个完美的世界中,你需要依赖这样的类来同资源进行交互,绝不要直接访问原生(raw)资源而玷污你的双手。但是世界不是完美的,许多API会直接引用资源,所以除非你放弃使用这样的API(这是不实际的想法),你将会绕开资源管理类而时不时的处理原生资源。

2. 如何获取原生资源——通过显示转换和隐式转换

2.1 一个例子

举个例子,Item 13中介绍了使用像auto_ptr或者tr1::shared_ptr这样的智能指针来存放调用createInvestment工厂函数的返回结果:

 std::tr1::shared_ptr<Investment> pInv(createInvestment()); // from Item 13

假设你想使用一个同Investment对象一起工作的函数,如下:

 int daysHeld(const Investment *pi); // return number of days

 // investment has been held

你会像下面这样调用它:

 int days = daysHeld(pInv); // error!

代码将不能通过编译:dayHeld想要使用一个原生Investment*指针,你却传递了一个tr1::shared_ptr<Investment>类型的对象。

你需要一种方法将一个RAII类对象(在这个例子中是tr1::shared_ptr)转换成它所包含的原生资源类型。有两种常见的方法来实现它:显示转换和隐式转换

2.2 使用智能指针的get进行显示转换

Tr1::shared_ptr和auto_ptr都提供了一个get成员函数来执行显示转换,也就是返回智能指针对象内部的原生指针:

 int days = daysHeld(pInv.get()); // fine, passes the raw pointer

 // in pInv to daysHeld

2.3 使用智能指针的解引用进行隐式转换

事实上像所有的智能指针一样,tr1::shared_ptr和auto_ptr也重载了指针的解引用运算符(operator->和operator*),这就允许将其隐式的转换成底层原生指针:

 class Investment { // root class for a hierarchy

 public: // of investment types

 bool isTaxFree() const;

 ...

 };

 Investment* createInvestment(); // factory function

 std::tr1::shared_ptr<Investment> // have tr1::shared_ptr

 pi1(createInvestment()); // manage a resource

 bool taxable1 = !(pi1->isTaxFree()); // access resource

 // via operator->

 ...

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

 // manage a

 // resource

 bool taxable2 = !((*pi2).isTaxFree()); // access resource

 // via operator*

2.3 自己实现get进行显示转换

因为有时候获取RAII对象中的原生资源是必要的,一些RAII类的设计者通过提供一个隐式转换函数来顺利达到此目的。举个例子,考虑下面的字体RAII类,字体对于C API来说是原生数据结构:

 FontHandle getFont(); // from C API — params omitted

 // for simplicity

 void releaseFont(FontHandle fh); // from the same C API

 class Font { // RAII class

 public:

 explicit Font(FontHandle fh) // acquire resource;

 : f(fh) // use pass-by-value, because the

 {} // C API does

 ~Font() { releaseFont(f ); } // release resource

 ... // handle copying (see Item 14)

 private:

 FontHandle f; // the raw font resource

 };

假设有大量的字体相关的C API用于处理FontHandles,因此会有频繁的需求将Font对象转换成FontHandles对象。Font类可以提供一个显示的转换函数,比如说:get:

 class Font {

 public:

 ...

 FontHandle get() const { return f; } // explicit conversion function

 ...

 };

不幸的是,如果它们想同API进行通讯,每次都需要调用get函数:

 void changeFontSize(FontHandle f, int newSize); // from the C API

 Font f(getFont());

 int newFontSize;

 ...

 changeFontSize(f.get(), newFontSize); // explicitly convert

 // Font to FontHandle

一些程序员发现显示请求这些转换是如此令人不愉快以至于不想使用RAII类。但是这会增加泄漏字体资源的机会,这正是设计Font类要预防的事情。

2.3 自己实现operator() 进行隐式转换

一种替代的方法是让Font提供一个隐式转换到FontHandle的函数:

 class Font {

 public:

 ...

 operator FontHandle() const // implicit conversion function
{ return f; } ... };

这会使C API的调用变得容易并且很自然:

 Font f(getFont());

 int newFontSize;

 ...

 changeFontSize(f, newFontSize); // implicitly convert Font

 // to FontHandle

缺点是隐式转换增加了出错的机会。举个例子,客户端本来想要一个Font却创建了一个FontHandle:

 Font f1(getFont());

 ...

 FontHandle f2 = f1; // oops! meant to copy a Font

 // object, but instead implicitly

 // converted f1 into its underlying

 // FontHandle, then copied that

现在程序拥有一个被Font对象 f1管理的FontHandle,但是直接使用f2也可以获得这个FontHandle。这就不好了。例如:当f1被销毁,字体资源被释放,f2就变成了悬挂指针。

3.隐式转换和显示转换如何选择?

提供从RAII类对象到底层资源的显示转换(通过一个get成员函数)还是提供隐式转换依赖于设计出来的RAII类需要执行的特殊任务以及使用的场景。最好的设计看上去要遵守Item 18的建议:使接口容易被正确使用,很难被误用。通常情况下,像get一样的显示转换函数会是更好的选择,因为它减少了类型误转换的机会。然而有时候,使用隐式类型转换的自然特性会使局面发生扭转。

4.访问原生资源和封装背道而驰?

函数返回一个RAII类中的原生资源同封装是背道而驰的,这已经发生了。这不是设计的灾难,RAII类的存在不是用来封装一些东西;他们的存在是用来保证资源的释放会发生。如果需要,资源封装可以在这个基本功能之上进行实现,但这不是必要的。此外,一些RAII类将实现的真正封装同底层资源非常松散的封装组合到一块。举个例子:tr1::shared_ptr封装了所有的引用计数,但是仍然可以非常容易的访问它所包含的原生指针。像一些设计良好的类,它隐藏了客户没有必要看到的东西,但是它提供了客户端确实需要访问的东西。

5.总结

  • API通常需要访问原生资源,所以每个RAII类应该提供一个获得它所管理的原生资源的方法。
  • 访问原生资源可以通过显式转换或者隐式转换来达到。一般情况下,显示转换更加安全,隐式转换对客户端来说更加方便。

读书笔记 effective c++ Item 15 在资源管理类中提供对原生(raw)资源的访问的更多相关文章

  1. 读书笔记 effective c++ Item 14 对资源管理类的拷贝行为要谨慎

    1. 自己实现一个资源管理类 Item 13中介绍了 “资源获取之时也是初始化之时(RAII)”的概念,这个概念被当作资源管理类的“脊柱“,也描述了auto_ptr和tr1::shared_ptr是如 ...

  2. Effective C++(15) 在资源管理类中提供对原始资源的访问

      问题聚焦:     资源管理类是为了对抗资源泄露.     如果一些函数需要访问原始资源,资源管理类应该怎么做呢?        关于资源管理的概念总是显得那么的高大上,其实只是抽象一点. 下面用 ...

  3. Effective C++ 条款15、16 在资源管理类中提供对原始资源的访问||成对使用new 与 delete要采取相同形式

    1.在资源管理类中提供对原始资源的访问     前几个条款很棒,它们是对抗资源泄露的壁垒,但很多APIs直接指向 资源,这个时候,我们需要直接访问原始资源.     这里,有两种方法解决上述问题,我们 ...

  4. 读书笔记 effective c++ Item 27 尽量少使用转型(casting)

    C++设计的规则是用来保证使类型相关的错误不再可能出现.理论上来说,如果你的程序能够很干净的通过编译,它就不会尝试在任何对象上执行任何不安全或无意义的操作.这个保证很有价值,不要轻易放弃它. 不幸的是 ...

  5. 读书笔记 effective c++ Item 44 将与模板参数无关的代码抽离出来

    1. 使用模板可能导致代码膨胀 使用模板是节省时间和避免代码重用的很好的方法.你不需要手动输入20个相同的类名,每个类有15个成员函数,相反,你只需要输入一个类模板,然后让编译器来为你实例化20个特定 ...

  6. 读书笔记 effective c++ Item 11 在operator=中处理自我赋值

    1.自我赋值是如何发生的 当一个对象委派给自己的时候,自我赋值就会发生: class Widget { ... }; Widget w; ... w = w; // assignment to sel ...

  7. 读书笔记 effective c++ Item 13 用对象来管理资源

    1.不要手动释放从函数返回的堆资源 假设你正在处理一个模拟Investment的程序库,不同的Investmetn类型从Investment基类继承而来, class Investment { ... ...

  8. 读书笔记 effective c++ Item 19 像设计类型(type)一样设计

    1. 你需要重视类的设计 c++同其他面向对象编程语言一样,定义了一个新的类就相当于定义了一个新的类型(type),因此作为一个c++开发人员,大量时间会被花费在扩张你的类型系统上面.这意味着你不仅仅 ...

  9. 读书笔记 effective c++ Item 29 为异常安全的代码而努力

    异常安全在某种意义上来说就像怀孕...但是稍微想一想.在没有求婚之前我们不能真正的讨论生殖问题. 假设我们有一个表示GUI菜单的类,这个GUI菜单有背景图片.这个类将被使用在多线程环境中,所以需要mu ...

随机推荐

  1. [转]解决Maven报错"Plugin execution not covered by lifecycle configuration"

    [转]解决Maven报错"Plugin execution not covered by lifecycle configuration" 导入Myabtis源码后,POM文件会报 ...

  2. 【转】C\C++代码优化的27个建议

    1. 记住阿姆达尔定律: funccost是函数func运行时间百分比,funcspeedup是你优化函数的运行的系数. 所以,如果你优化了函数TriangleIntersect执行40%的运行时间, ...

  3. realvnc viewer 5.3.2无需输入用户名和密码访问远程桌面

    我从https://www.realvnc.com/download/viewer/下载了realvnc viewer用于访问远程的Linux桌面,这个版本不需要安装,直接运行就可以了.但在访问远程桌 ...

  4. CLR VIA C#: 基元类型、 引用类型 和 值类型

    一.基元类型 . 引用类型 和 值类型的区别: 1.基元类型(primitive type):编译器直接支持的数据类型: 基元类型 直接映射到 FCL 中存在的类型. C# 小写是基元类型,例如:st ...

  5. 《Web接口开发与自动化测试 -- 基于Python语言》 ---前言

    前    言 本书的原型是我整理一份Django学习文档,从事软件测试工作的这六.七年来,一直有整理学习资料的习惯,这种学习理解再输出的方式对我非常受用,博客和文档是我主要的输出形式,这些输出同时也帮 ...

  6. 关于Apache,Mysql,PHP之间的关系

    声明:以下为作者原创,转载请注明文章来源地址. 通过百度百科我们知道 Apache(全称Apache HTTP Server):是世界使用排名第一的Web服务器软件.可以在大多数计算机操作系统中运行, ...

  7. IOS 创建和设置pch

    1.添加pch文件 2.修改工程配置文件 Building Settings->All->Apple LLVM 6.0 -Language -> Prefix Header

  8. 如何设置打开jsp页面速度加快?

    1.

  9. Java语言Socket接口用法详解

    Socket接口用法详解   在Java中,基于TCP协议实现网络通信的类有两个,在客户端的Socket类和在服务器端的ServerSocket类,ServerSocket类的功能是建立一个Serve ...

  10. HDU-2060-Snooker

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2060 题意: 给你场上剩下的球数m , 和 a ,b 两名队员目前得分,现在假设a将 所有的球m都打入 ...