上一篇:C++对象布局及多态之虚成员函数调用 >>
C++箴言:在资源管理类中准备访问裸资源
| 天极软件专题专区精选 | ||
| Google专区 | POPO专区 | QQ专区 |
| Flash MX 视频教程 | Photoshop视频教程 | 网页设计视频教程 |
| PowerPoint动画演示教程 | Excel动画教程集 | Word动画演示教程 |
| 特洛伊木马专区 | 黑客知识教程专区 | 注册表应用专区 |
| Windows API开发专区 | 网络编程专区 | VB数据库编程专区 |
资源管理类真是太棒了。他们是你防御资源泄漏的防波堤,没有这样的泄漏是设计良好的系统的基本特征。在一个完美的世界中,你可以在所有与资源的交互中依赖这样的类,从来不需要因为直接访问裸资源(raw resources)而玷污你的手。但是这个世界并不完美,很多 API 直接涉及资源,所以除非你计划坚决放弃使用这样的 API(这种事很少会成为实际),否则,你就要经常绕过资源管理类而直接处理裸资源(raw resources)。
例如,以前介绍的使用类似 auto_ptr 或 tr1::shared_ptr 这样的智能指针来持有调用类似 createInvestment 这样的 factory 函数的结果: std::tr1::shared_ptr<Investment> pInv(createInvestment());
假设你打算使用的一个与 Investment 对象一起工作的函数是这样的:
| int daysHeld(const Investment *pi); // return number of days // investment has been held |
你打算像这样调用它,
| int days = daysHeld(pInv); // error! |
但是这代码不能编译:daysHeld 要求一个裸的 Investment* 指针,但是你传给它一个类型为 tr1::shared_ptr<Investment> 的对象。
你需要一个将 RAII 类(当前情况下是 tr1::shared_ptr)的对象转化为它所包含的裸资源(例如,底层的 Investment*)的方法。有两个常规方法来做这件事。显式转换和隐式转换。
tr1::shared_ptr 和 auto_ptr 都提供一个 get 成员函数进行显示转换,也就是说,返回一个智能指针对象内部的裸指针(raw pointer)(或它的一个副本):
| int days = daysHeld(pInv.get()); // fine, passes the raw pointer // in pInv to daysHeld |
就像实际上的所有智能指针类一样,tr1::shared_ptr 和 auto_ptr 也都重载了指针解引用操作符(pointer dereferencing operators)(operator-> 和 operator*),而这样就允许隐式转换到底层的裸指针(raw pointers):
| 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* ... |
因为有些时候有必要取得 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 private: FontHandle f; // the raw font resource }; |
假设有一个巨大的与字体有关的 C++ API 只能与 FontHandle 打交道,这就需要频繁地将 Font 对象转换为 FontHandle。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 |
一些程序员可能发现对显式请求这个转换的需求足以令人郁闷而避免使用这个类。反过来,设计 Font 类又是为了预防泄漏字体资源的机会的增长。
可选择的办法是为 Font 提供一个隐式转换到它的 FontHandle 的转换函数:
| class Font { public: ... operator FontHandle() const { return f; } // implicit conversion function ... }; |
这样就可以使对 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,但是这个 FontHandle 也能通过直接使用 f2 来加以利用。这几乎绝对不会成为什么好事。例如,当 f1 被销毁,字体将被释放,f2 则被悬挂(dangle)。
关于是否提供从一个 RAII 类到它的底层资源的显式转换(例如,通过一个 get 成员函数)或者允许隐式转换的决定,要依靠 RAII 类被设计履行的具体任务和它被计划使用的细节而做出。最好的设计很可能就是坚持 Item 18 的建议(使接口易于正确使用,而难以错误使用)的那一个。通常,类似 get 的一个显式转换函数是更可取的方式,因为它将意外的类型转换的机会减到最少。偶尔的,通过隐式类型转换提高使用的自然性将使天平向那个方向倾斜。
你可能已经意识到,函数返回一个 RAII 类内部的裸资源破坏了封装。这是正确的,但这并非像它开始看上去那样是个设计的祸患。RAII 类的存在并非为了封装什么东西;它的存在是为了确保一个特殊的动作——资源释放——的发生。如果你希望,封装资源的地位也可以提高到这个主要功能之上,但这并非必需。此外,一些 RAII 类将实现的真正封装和底层资源的非常宽松的封装结合在一起。例如,tr1::shared_ptr 封装了它的引用计数的全部机制,但它依然提供对它所包含的资源的简单访问。就像大多数设计良好的类,它隐藏了客户不需要看到的,但它也让客户的确需要访问的那些东西可以利用。
Things to Remember
·API 经常需要访问裸资源,所以每一个 RAII 类都应该提供取得它所管理的资源的方法。
·访问可以通过显式转换或者隐式转换进行。通常,显式转换更安全,而隐式转换对客户来说更方便。
下一篇:C++箴言:了解C++偷偷加上和调用了什么 >>
相关文章:
- · ASP.NET入门随想之金庸群“粒”传
- · 实现IT创业的十三种模式分析
- · ASP.NET中随机数生成及应用
- · ASP.NET 翻页后继续维持排序
- · 服务器市场IBM领跑 Window系统夺冠
- · 天衣无缝:IIS与PHP水火也相容
- · 如何在IIS上搭建WAP网站
- · 借周杰伦新专辑传播 毒帖现张靓颖官网
- · 明晰程序异常的来源 ASP错误处理小结
- · 韩国研发本土搜索工具 应对海外入侵
- · Hotmail故障引连锁反应 垃圾邮件作祟
- · 谁应该对软件代码中安全漏洞负责
- · 美空军数据库被窃 三万多军官资料被盗
- · 数据库系统 防止黑客入侵之技术综述
- · IE等存在安装漏洞 黑客可远程接入PC
- · 安全技巧:寻找客户端漏洞的艺术
- · IE再现安全漏洞 XP SP2易受远程攻击
- · 实例分析 利用微软漏洞提升电脑控制权限
- · 识别常见Web应用安全漏洞 有效防止入侵
- · 杜绝入侵:八大法则防范ASP网站漏洞
- · 思科交换机如何防范典型欺骗和攻击(图)
- · GFI在邮件内容过滤软件中增加新杀毒引擎
- · 雅虎邮箱爆脚本攻击漏洞 所幸无用户遭袭
- · Skype出现严重安全漏洞 影响两大系统
- · 甲骨文与MySQL谈判 收购矛头指向开源
- · Oracle表示可能会推出自有Linux版本
- · 最新的黑客技术:详解XSS跨站脚本攻击
- · WMF漏洞木马呈蔓延之势 春节需重点防范
- · 综述:2006搜索市场谁主沉浮
- · 欧洲研发Google杀手多媒体搜索开发
- · Oracle加入Grails开源计划
- · Oracle发布密码工具补默认密码漏洞
- · 探讨Oracle数据库的数据导入方法
- · Oracle9i Data Guard的灾难防护
- · Oracle JDeveloper10.1.3预览版发布
- · Oracle收购Siebel后的六大猜想
- · 破解Oracle中国高层频繁变动之谜
- · 数据库引擎被甲骨文操控 MySQL另谋后台
