条款13 以对象管理资源

记住:

★为防止资源泄漏,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放

★两个常被使用的RAII classes分别是tr1::shared_ptr和auto_ptr。前者通常是较佳选择,∵其copy行为比较直观。若选择auto_ptr,复制动作会使它(被复制物)指向null

------------------------------------------------------------------------

为确保资源总是被释放,应将资源放进对象内,当控制流离开后,该对象的dtor会自动释放那些资源。以对象管理资源的两个关键想法:

  (1)获得资源后立刻放进管理对象;

  (2)管理对象运用dtor确保资源被释放

auto_ptr特殊性质:通过copying函数复制它们,它们会变成NULL,而复制所得的指针将取得资源的唯一拥有权:

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

    std::auto_ptr<Investment> pInv2( pInv1 );  //现pInv2指向对象,而pInv1为null!!!

可见受auto_ptr管理的资源必须绝对没有一个以上的auto_ptr同时指向它。

auto_ptr的一个替代方案是“引用计数型智能指针”,如tr1::shared_ptr。即该智能指针持续追踪共有多少个对象指向某笔资源,并在无人指向它时自动删除该资源。∴其copying行为正常很多:

    std::tr1::shared_ptr<Investment> pInv2( pInv1 ); //pInv1和pInv2指向同一个对象

auto_ptr和shared_ptr两者都在其destructor内做delete而不是delete[]。这意味着在动态分配而得的array身上使用以上两者十分不好。尽管如此,其仍然能通过编译:

    std::auto_ptr<std::string> aps( new std::string[10] );

条款14 在资源管理类(自己建立的)中小心copying行为

记住:

★复制RAII对象必须一并复制它所管理的资源,∴资源的copying行为决定RAII对象的copying行为

★普遍而常见的RAII class copying行为是:抑制copying、引用计数法。不过其他行为也都可能被实现

--------------------------------------------------------------------------------

举例:

  为确保不忘记将一个被锁住的Mutex解锁,可建立一个class来管理机锁。这样的class基本结构由RAII守则支配:资源在构造期间获得,在析构期间释放

  1. class Lock {
  2. public:
  3. explicit Lock( Mutex *pm ) : mutexPtr( pm ) {
  4. lock( mutexPtr ); //获得资源
  5. }
  6.  
  7. ~Lock() {
  8. unlock( mutexPtr ); //释放资源
  9. }
  10. private:
  11. Mutex *mutexPtr; //raw资源
  12. };

客户使用时:

  1. Mutex m;
  2. ...
  3. { //建立一个区块来定义critical section
  4. Lock ml( &m ); //锁定互斥器
  5. ... //执行critical section内的操作
  6. } //区块最末尾自动解除互斥器锁定

但若Lock对象被复制,会发生何事???

当一个RAII对象被复制,会有两种处理方式:

方式一:禁止复制

将copying操作声明为private,所以对Lock而言看起来如下:

    class Lock : private Uncopyable {

    ...

    };

方式二:对底层资源祭出“引用计数法”

希望保有资源直到它的最后一个使用者(某对象)被销毁。复制RAII对象时应将该资源的被引用数递增。tr1::shared_ptr便是如此。

对方法二,通常RAII类中只要内含一个tr1::shared_ptr即可实现reference-counting copying行为。而此处要定制tr1::shared_ptr的“删除器”,当引用次数为0时调用(∵tr1::shared_ptr的默认行为是当引用次数为0时删除其所指物):

  1. class Lock {
  2. public:
  3. explicit Lock( Mutex *pm ) : mutexPtr( pm, unlock ) {
  4.  
  5. lock( mutexPtr.get() ); //get函数返回sp内部raw指针(的复件)
  6. }
  7.  
  8. private:
  9. std::tr1::shared_ptr<Mutex> mutexPtr; //用智能指针替换raw pointer
  10. };

此处无需dtor!因为编译器生成的dtor会自动调用其non-static成员(本例的mutexPtr)的dtor,而mutexPtr的dtor会在互斥器的引用次数为0时自动调用tr1::shared_ptr的删除器(本例为unlock)。

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

记住:

★APIs往往要求访问原始资源,∴每个RAII class应提供一“取得其所管理之资源”的办法

★对原始资源的访问可能经由显式转换或隐式转换。一般而言显式转换较安全,但隐式转换对客户较方便。

---------------------------------------------------------------------------------

有两方法可以将RAII class对象转换为其所内含之原始资源:显式隐式转换

举例:

  1. class Font {
  2.  
  3. public:
  4. explicit Font( FontHandle fh ):f( fh ) {} //获得资源
  5. ~Font() { releaseFont(f); } //释放资源
  6. private:
  7. FontHandle f; //原始字体资源
  8. };

假设有大量与字体相关的C API处理的是FontHandles,那么就需将Font对象转换为FontHandle。Font class有两种做法:

方法一:提供显式转换函数:

  1. class Font {
  2.  
  3. public:
  4. ...
  5. FontHandle get() const { return f; } //显式转换函数
  6. ...
  7. };

客户使用时:

  1. void changeFontSize( FontHandle f, int newSize ); //C API,需要原始资源
  2. Font f( getFont() ); //获取字体资源,Font是资源管理类
  3. int newFontSize;
  4. ...
  5. changeFontSize( f.get(), newFontSize ); //明白地将Font转换为FontHandle

方法二:提供隐式转换函数:

  1. class Font {
  2.  
  3. public:
  4. ...
  5. operator FontHandle() const { return f; } //隐式转换函数
  6. ...
  7. };

客户使用时:

changeFontSize( f, newFontSize ); //会将Font隐式转换为FontHandle

注:

tr1::shared_ptr和auto_ptr都提供一个get成员函数,用来执行显式转换,其会返回智能指针内部的原始指针(的复件)。而且tr1::shared_ptr和auto_ptr也重载了指针取值操作符(operator->和operator*),它们允许隐式转换至底部原始指针:

std::tr1::shared_ptr<Investment> pi1( createInvestment() );  //令tr1::shared_ptr管理一笔资源

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

                      //或bool taxlabel  = !( (*pi1).isTaxFree() ); //经由operator*访问资源

补充:

RAII class内的那个返回原始资源的函数,确实是与“封装”思想矛盾。但RAII并非为了封装某物而存在,所以也没关系。就像很多设计良好的classes一样,它隐藏了客户不需要看的部分,但备妥客户需要的所有东西。

EC读书笔记系列之8:条款13、14、15的更多相关文章

  1. EC读书笔记系列之17:条款41、42、43、44、45、46

    条款41 了解隐式接口与编译器多态 记住: ★classes和templates都支持接口和多态 ★对classes而言接口是显式的(explicit),以函数签名为中心.多态则是通过virtual函 ...

  2. EC读书笔记系列之13:条款25 考虑写出一个不抛异常的swap函数

    记住: ★当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定其不抛出异常 ★若你提供一个member swap,也该提供一个non-member swap来调用前者.对于cla ...

  3. EC读书笔记系列之16:条款35、36、37、38、39、40

    条款35 考虑virtual函数以外的其他选择 记住: ★virtual函数的替代方案包括NVI手法及Strategy模式的多种形式.NVI手法自身是一个特殊形式的Template Method模式 ...

  4. EC读书笔记系列之1:条款1、条款2、条款3

    条款1:视C++为一个语言联邦 记住: ★C++高效编程守则视状况而变化,这取决于你使用C++的哪一部分 C: Object-oriented c++: Template c++: STL 条款2:尽 ...

  5. EC读书笔记系列之20:条款53、54、55

    条款53 不要轻忽编译器的警告 记住: ★严肃对待编译器发出的警告信息.努力在你的编译器的最高(最严苛)警告级别下争取“无任何警告”的荣誉 ★不要过度依赖编译器的报警能力,∵不同的编译器对待事情的态度 ...

  6. EC读书笔记系列之19:条款49、50、51、52

    条款49 了解new-handler的行为 记住: ★set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用 ★Nothrow new是一个颇为局限的工具,∵其只适用于内存 ...

  7. EC读书笔记系列之18:条款47、48

    条款47 请使用traits classes表现类型信息 记住: ★Traits classes使得“类型相关信息”在编译期可用.它们以templates和“templates特化”完成实现 ★整合重 ...

  8. EC读书笔记系列之15:条款32、33、34

    条款32 确保你的public继承塑模出is-a关系 记住: ★public继承意味着is-a.适用于base class身上的每一件事情一定也适用于derived class身上,∵每一个deriv ...

  9. EC读书笔记系列之14:条款26、27、28、29、30、31

    条款26 尽可能延后变量定义式的出现时间(Lazy evaluation) 记住: ★尽可能延后变量定义式的出现.这样做可增加程序的清晰度并改善程序效率 ----------------------- ...

随机推荐

  1. UVA10869 - Brownie Points II(线段树)

    UVA10869 - Brownie Points II(线段树) 题目链接 题目大意:平面上有n个点,Stan和Ollie在玩游戏,游戏规则是:Stan先画一条竖直的线作为y轴,条件是必需要经过这个 ...

  2. 链接脚本之LMA VMA解释

    链接脚本中的LMA和VMA是什么意思.这个问题纠结了一段时间,今天在看<ARM体系结构与编程>时,豁然开朗,写下自己的认识.分享例如以下: LMA:载入地址 位于存储器中的地址  LOAD ...

  3. android典型监听事件实

    public class MainActivity extends Activity { int counter; Button add, sub; TextView display; @Overri ...

  4. HTML5开发 BUG解决

    1.点透Q:元素A上定位另外一个元素B,点击元素B,如果元素A有事件或链接,会触发元素A上的事件或链接,即点透A:在元素B的touchend中增加ev.preventDefault();阻止默认事件即 ...

  5. Result Cache结果高速缓存 (转)

    1.1 概述 SQL 查询结果高速缓存可在数据库内存中对查询结果集和查询碎片启用显式高速缓存.存储在共享池(Share Pool)中的专用内存缓冲区可用于存储和检索高速缓存的结果.对查询访问的数据库对 ...

  6. css overflow

    overflow-x,overflow-y的值相同等同于overflow overflow-x,overflow-y的值不同,其中一个被赋值visible,另一个被赋值为auto,scroll,hid ...

  7. DW 做一个table表 对单元格进行合并

    编辑前的代码 <body> <table width="500" border="0" bgcolor='#000000' backgroun ...

  8. C/S ASP.NET页面传值汇总

    一. QueryString是一种非常简单的传值方式,他可以将传送的值显示在浏览器的地址栏中.如果是传递一个或多个安全性要求不高或是结构简单的数值时,可以使用这个方法.但是对于传递数组或对象的话,就不 ...

  9. 阿里大鱼短信接口整合Tp3.2.3开发整理

    阿里大鱼 http://www.alidayu.com/ 的短信接口总体是不错的,别安驹个人认为不管是从性价比还是稳定性上都是跟同类的短信接口好些,毕竟是大公司的东西不会差到哪去.下面把之前开发的短信 ...

  10. Eclipse用link方式安装插件

    其实eclipse安装插件更方便的方法就是直接扔到eclipse目录下的dropins文件夹,但如果插件比较多或者大的话,会让eclipse变得臃肿.下面介绍的用link方式可以避免这样的问题. 用l ...