引言

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

例如在条款13中引入了智能指针如auto_ptr或tr1::shared_ptr保存factory函数如createInvestment的调用结果:

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

有某个处理函数:

int daysHeld(const Investment* pi);

如果我们这样调用函数:

int days = daysHeld(pInv); //无法通过编译!

因为daysHeld需要的是Investment* 指针,但是你传给它的却是个类型为tr1::shared_ptr<Investment>的对象。

这种时候需要一个函数将RAII class对象(本例为tr1::shared_ptr)转换为其所内含的原始资源(本例为Investment*)达成目标有两个做法:显式转换和隐式转换。

■显示转换

提供一个get成员函数,用来执行显式转换。例如tr1::shared_ptr和auto_ptr都提供一个get成员函数来返回智能指针内部的原始指针(副本):

int days = daysHeld(pInv.get());

■隐式转换

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

class Investment {
public:
bool isTaxFree() const;
...
}; Investment* createInvestment(); std::tr1::shared_ptr<Investment> pi1(createInvestment());
bool taxable1 = pi1->isTaxFree(); // 使用operator->访问资源 std::auto_ptr<Investment> pi2(createInvestment());
bool taxable2 = (*pi2).isTaxFree(); // 使用operator*访问资源

上面的隐式转换依然无法满足daysHeld()函数的要求,这时候我们必须得取得RAII对象内的原始资源。
使用显示转换方法来满足必须调用get()函数,到处如此这般要求使资源管理类使用起来可能让人产生不悦!

另一个做法是提供一个隐式转换函数:

FontHandle getFont();                             // 这是个C API,参数已经简化
void releaseFont(FontHandle fh); // 来自同一组C API
void changeFontSize(FontHandle f, int newSize); // 来自同一组C API class Font {
public:
explicit Font(FontHandle fh) // 获得资源
:f(fh)
{} ~Font() {releaseFont(f);} // 释放资源 FontHandle get() const {return f;} // 显式转换函数
operator FontHandle() const{return f;} // 隐式转换函数 private:
FontHandle f;
}

使用场景

int newFontSize;
Font f1(getFont());
...
changeFontSize(f1.get(),newFontSize); // 显式转换 Font f2(getFont());
changeFontSize(f2,newFontSize); // 隐式转换

但是这个隐式转换会增加错误发生机会。例如客户可能会在需要Font时意外得到一个FontHandle:

Font f1(getFont());
...
FontHandle f2 = f1; // 原意是要拷贝一个Font对象,却反而将f1隐式转换为其底部的FontHandle,
// 却反而将f1隐式转换为其底部的FontHandle,
// 然后才复制它。

以上程序有个FontHandle由Font对象f1管理,但那个FontHandle也可以通过直接使用f2取得。
如果当f1被销毁,字体被释放,而f2因此成为"虚吊的"(dangle)

◆总结

1.APIs往往要求访问原始资源(raw resources),所以每个RAII class应该提供一个"取得其所管理的资源"的办法
2.显示转换比较安全,隐式转换对客户比较方便

[Effective C++ --015]在资源管理类中提供对原始资源的访问的更多相关文章

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

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

  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 C++(14) 在资源管理类中小心copying行为

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

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

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

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

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

随机推荐

  1. 【Grunt】

    GRUNT The JavaScript Task Runnerhttp://gruntjs.com/ Grunt打造前端自动化工作流http://tgideas.qq.com/webplat/inf ...

  2. 算法的时间复杂度(大O表示法)

    定义:如果一个问题的规模是n,解这一问题的某一算法所需要的时间为T(n),它是n的某一函数 T(n)称为这一算法的“时间复杂性”. 当输入量n逐渐加大时,时间复杂性的极限情形称为算法的“渐近时间复杂性 ...

  3. Can not perform pod install under el capitan (15A279b)

    这个问题在stackoverflow上面有过讨论: Can not perform pod install under el capitan (15A279b) 被采纳的答案为:sudo gem in ...

  4. (2)java堆内存

    java堆内存结构图 [名词解释]--->eden,so,s1通称为新生代对象储区--->tenured称为老年代对象存储区--->s0和s1也称为from和to区域,是两块大小相等 ...

  5. 【转】ubuntu下自动挂载硬盘分区

    1, 首先摸清我系统的硬盘分区等使用情况 sudo fdisk -l Disk /dev/sda: 128.0 GB, 128035676160 bytes 255 heads, 63 sectors ...

  6. POJ3087 Shuffle'm Up 简单模拟

    题意:就是给你两副扑克,然后一张盖一张洗牌,不断重复这个过程,看能不能达到目标的扑克顺序 分析:然后就模拟下,-1的情况就是有循环节 #include<cstdio> #include&l ...

  7. bzoj 1034 [ZJOI2008]泡泡堂BNB(贪心)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1034 [题意] 给两个序列以任意顺序比较,求出最大和最小得分. [思路] 排序后使用贪 ...

  8. 清空具有外键约束的表时报ERROR 1701(42000)的解决办法

    ERROR 1701 (42000): Cannot truncate a table referenced in a foreign key constraint (`furion`.`tbl_fr ...

  9. HIbernate学习笔记(七) hibernate中的集合映射和继承映射

    九.       集合映射 1. Set 2. List a)        @OrderBy 注意:List与Set注解是一样的,就是把Set更改为List就可以了 private List< ...

  10. 有关ftp批量传送文件或文件夹

    以ftp 批量上传文件时 可以用 mput file1 file2 .. 但是这样没传送一个就会问是否传送下一个 ,那么可以使用prompt这个命令.这是个双向开关,执行一次是取消提示,在执行一次是打 ...