问题聚焦:
    资源管理类是为了对抗资源泄露。
    如果一些函数需要访问原始资源,资源管理类应该怎么做呢?
 
    

关于资源管理的概念总是显得那么的高大上,其实只是抽象一点。
下面用一个例子还说明本节的主题:
在前面 Effective C++(13) 用对象管理资源 中我们提到过,使用智能指针保存工厂函数返回的结果
std::tr1::shared_ptr<Investment> pInv(createInvestment());

//加入有某个函数,用来处理Investment对象,像下面这样
int daysHeld(const Investment* pi); // 返回投资天数 //那么像下面这样的调用时错误的
int days = daysHeld(pInv);
错误原因很明显,daysHeld需要的是Investment* 指针,而传给它的却是类型为tr1::shared_ptr<Investment>的对象。
这时候,需要的是一个类型转换,将tr1::shared_ptr<Investment>类型的指针转换为Investment*类型的指针。

有两个做法可以达到这个目标:显示转换和隐式转换。

显示转换

tr1::shared_ptr和auto_ptr都提供一个get成员函数,用来执行显式转换,也就是,它会返回智能指针内部的原始指针。
int days = daysHeld(pInv.get());    // 将pInv内的原始指针传给了daysHeld

隐式转换
几乎所有的智能指针都重载了指针取值操作符(operator->和operator*),
它们允许将当前智能指针隐身转换为它管理的资源的指针。
class Investment
{
public:
bool isTaxFree() const; // 被管理资源内部定义的一个方法
... ...
}; /*使用*/
Investment* createInvestment();
std::tr1:;shared_ptr<Investment> pi1(createInvestment());
bool taxable = !(pi1->isTaxFree());
......
std::auto_ptr<Investment> pi2(createInvestment());
bool taxable = !((*pi2).isTaxFree());
......


如果让你设计一个RAII class,可能会像下面这个管理字体资源的例子中那样,提供一个显式转换或者隐式转换。
我喜欢贴上一大段的代码,说再多理论不如一段代码,所以不理解的地方请注意上下的注释。
// 类设定
FontHandle getFont(); // 获取字体资源函数
void releaseFont(FontHandle fh); // 释放字体资源 class Font
{
public:
explicit Font(FontHandle fh)
: f(fh)
{ }
~Font()
{
releaseFont(f);
}
private:
FontHandle f;
}; /*使用该类*/
// 假如有大量与字体相关的C API,它们处理的是FontHandles,那么“将Font对象转换为FontHandle”会是一种很频繁的需求。 // 提供一个显式转换
class Font
{
public:
... ...
FontHandle get() const { return f; } // 显示转换函数
... ...
}; // 使用该显式转换
void changeFontSize(FontHandle f, int newSize); // C API(系统给定) Font f(getFont());
int newFontSIze;
... ...
changeFontSize(f.get(), newFontSize); // 频繁地调用这个get方法会让人容易厌烦
// 下面提供一个隐式转换
class Font
{
public:
...
operator FontHandle() const { return f; }
...
}; //使用该隐式转换
Font f(getFont());
int newFontSize;
...
changeFontSize(f, newFontSize); // 但是这个隐式转换会增加错误发生机会
Font f1(getFont());
...
Font f2 = f1; // 本意死打算拷贝一个Font对象,结果却将f1隐式转换为底部的FontHandle才复制,编译不通过
FontHandle f3 = f1; // 现在类型匹配没问题了吧,但是当f1被销毁,其内部的资源也同时被销毁,那么这个f3指向的资源也同时被释放。


看了上面这个例子可能你比较迷惑,你可能会问,到底是用显式转换还是隐式转换啊?
其实,答案主要取决于特定的场景和使用情况。
最佳的设计是要遵守一个原则:让接口容易正确使用,不易被误用。

敏感的同学可能注意到,返回原始资源的这个用法,和“封装”这个目的发生了矛盾。
这里,我们需要明确一点的是,资源管理类的存在不是为了封装某物而存在的,它们的存在是为了确保一个特殊行为——资源释放,一定会发生。
设计良好的classes,是隐藏了客户不care的部分,而备妥客户需要的所有东西。

小结:
  • APIs往往要求访问原始资源,所以,每一个资源管理类应该提供一个取得其所管理的资源的方法。
  • 对原始资源的访问可能经由显示转换或隐式转换。一般而言,显式转换比较安全,但隐式转换对客户比较方便。
参考资料:
《Effective C++ 3rd》

Effective C++(15) 在资源管理类中提供对原始资源的访问的更多相关文章

  1. [Effective C++ --015]在资源管理类中提供对原始资源的访问

    引言 资源管理类是防止资源泄漏的有力武器,但是许多APIs直接指涉资源,除非你发誓永不使用这样的APIs,否则只得绕过资源管理对象(resource-managing objects)直接访问原始资源 ...

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

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

  3. Effective C++ -----条款15:在资源管理类中提供对原始资源的访问

    APIs往往要求访问原始资源(raw resources),所以每一个RAII class应该提供一个“取得其所管理之资源”的办法. 对原始资源的访问可能经由显示转换(.get()成员函数或者指针取值 ...

  4. 条款15:在资源管理类中提供对原始资源的访问(Provide access to raw resources in resource-managing classes)

    NOTE: 1.APIs往往要求访问原始资源(raw resources),所以每一个RAII class应该提供一个“取得其所管理之资源”的办法. 2.对原始资源的访问可能经由显示转换或隐式转换.一 ...

  5. 读书笔记_Effective_C++_条款十五:在资源类管理类中提供对原始资源的访问

    void f(int* a) { cout <<* a << endl; } int main() { shared_ptr<int> p(new int(3)); ...

  6. 读书笔记 effective c++ Item 15 在资源管理类中提供对原生(raw)资源的访问

    1.为什么需要访问资源管理类中的原生资源  资源管理类是很奇妙的.它们是防止资源泄漏的堡垒,没有资源泄漏发生是设计良好的系统的一个基本特征.在一个完美的世界中,你需要依赖这样的类来同资源进行交互,绝不 ...

  7. effective条款15,在资源管理类中小心copying行为

    class A { private: int *p; void lock(){ cout << p << "is lock" << endl; ...

  8. Effective C++(14) 在资源管理类中小心copying行为

    问题聚焦:     上一条款所告诉我们的智能指针,只适合与在堆中的资源,而并非所有资源都是在堆中的.     这时候,我们可能需要建立自己的资源管理类,那么建立自己的资源管理类时,需要注意什么呢?. ...

  9. [Effective C++ --014]在资源管理类中小心copying行为

    第一节 <背景> 条款13中讲到“资源取得的时机便是初始化时机”并由此引出“以对象管理资源”的概念.通常情况下使用std中的auto_ptr(智能指针)和tr1::shared_ptr(引 ...

随机推荐

  1. Java虚拟机参数设置(转)

    今天在加载一幅图片时,eclipse报出如下错误: “Exception in thread "main" java.lang.OutOfMemoryError: Java hea ...

  2. Unity3D游戏开发最佳实践20技能(两)

    [扩展和MonoBehaviourBase] 21.扩展一个自己的Mono Behaviour基类.然后自己的全部组件都从它派生 这能够使你方便的实现一些通用函数.比如类型安全的Invoke.或者是一 ...

  3. eclipse 配置android sdk和maven

    首先下载 ADT-22.2.0.rar eclipse-jee-kepler-R-win32-x86_64.zip android SDK4.2.zip 分别解压在一个盘 将ADT里面的两个目录内容相 ...

  4. 写得好 git 提交信息

    编写好 git 提交信息 提交信息 我们作出答复,更改将提交相关信息,这些信息通常被认为是重要的信息会小心留下应该离开,你为什么需要这个提交实例,提交解决任何问题. 我们需要良好的信息组织,虽然后来, ...

  5. ENode框架Conference案例转载

    ENode框架Conference案例分析系列之 - Quick Start 前言 前一篇文章介绍了Conference案例的架构设计,本篇文章开始介绍Conference案例的代码实现.由于代码比较 ...

  6. 小工具:内存监视器(SystemMonitor)

    卸了360之后,与之捆绑的加速球也没了.加速球可以查看剩余内存量,清理残留进程,有的时候不觉得这小玩意有多大作用,卸了之后才知道后悔. 加速球的替代方案比比皆是,如Windows自带的任务管理器,窗口 ...

  7. C++语言债券系列之十一——友元函数和拷贝构造函数

    1.好友功能 (1)友元函数类的普通功能外定义. 定义友元函数和相同的正常功能.在类必须声明的正常功能为好友. (2)友元函数不是一个成员函数. 你不能反对打电话.但直接调用:友元函数访问类的公共.p ...

  8. LINUX2.4.x网络安全框架

       在分析LINUX2.4.x网络安全的实现之前先简介一下它里面包括的几个重要概念:netfilter.iptables.match.target.nf_sockopt_ops.网络安全功能点的实现 ...

  9. NYOJ 330 一个简单的数学

    一个简单的数学题 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描写叙述 zyc近期迷上了数学,一天,dj想出了一道数学题来难住他.算出1/n,但zyc一时答不上来希望大家能 ...

  10. Cookie基础

    周末百度笔试,答得题都会,就是不仔细不心细,提前一个小时交卷子,想起来就已经晚了.问了一个cookie的问题,我SB的蒙住了,于是乎,似乎是跪掉了,回来后总结了下Cooke的相关问题.###获取coo ...