More Effective C++

#@author:       gr
#@date: 2015-05-24
#@email: forgerui@gmail.com

九、利用destructors避免泄漏资源

所谓RAII即"资源获取即是初始化的时候",所以就必须对资源进行释放。以一个对象存放资源,并依赖对象的析构函数释放资源。

把资源封装到对象体内,这样在发生异常时,对象析构时调用析构函数,释放资源,可以避免资源泄漏。

如果异常是在资源获取过程中抛出的,查看第十条;如果异常是在析构过程中发生的,查看第十一条。

十、在constructors内阻止资源泄漏

如果一个函数在constructor中发生了异常,它的析构函数不会被调用,因为这个对象的构造并没有完成。所以我们指望析构函数来帮我们清理。

BookEntry::BookEntry(const string& name, address, const string& imageFileName, const string& audioClipFileName)
:theName(name), theAddress(address), theImage(0), theAudioClip(0)
{
theImage = new Image(imageFileName);
theAudioClip = new AudioClip(audioClipFileName); //如果AudioClip对象构造出现异常,那么构造好的Image对象将无法释放
}

智能指针取代"pointer class members",这样以局部对象管理资源,当发生异常,BookEntry自动销毁时,也不要手动删除它们所指的对象。

十一、禁止异常(exception)流出destructors之外

调用destructor有两种情况:

  1. 对象在正常状态下被销毁
  2. 对象被exception处理机制销毁

我们讨论异常时的情况,在外面的catch块中调用析构函数,这样如果析构函数报错,还是会调用std::terminate()终止程序。避免这种情况,可以在catch块中再加一个try-catch,但这样做显得有些极端。方法是在可能产生异常的析构函数中进行异常的捕获,并且什么也不做。

Session::~Session()
{
try{
logDestruction(this);
}catch(...){}
}

十二、了解 "抛出一个exception" 与 "传递一个参数" 或 "调用一个虚函数" 之间的差异

  1. catch子句的参数类型

    使用pass by reference方法,可以对异常的修改起作用并且提高效率,一般将catch的参数类型定义为引用类型。

  2. 查找匹配的catch子句

    a. 类型转换支持的比函数参数匹配的少,不支持算术类型转换和类类型转换,只支持如下:

    • 非常量向常量转型
    • 派生类向基类转型
    • 数组被转换成指向数组类型的指针

    b. 匹配顺序

    函数匹配是best-fit进行的,选择匹配参数最好的,而异常问题是按first fit进行,按照顺序进行,所以要把子类(及特殊类型)放在通用类型的前面。

  3. 异常抛出

    异常抛出:

     Exception ex;
    throw ex;

    异常抛出时,不论catch是以什么方式捕获by referenceby value都需要进行复制,到catch子句上的正是这个异常的副本。

    异常重新抛出:

     catch (exception& ex)
    {
    //....
    throw; //重新抛出此exception,使它继续传播
    }
    catch (exception& ex)
    {
    //....
    throw ex; //传播被捕捉的exception的一个副本
    }

十三、以by reference方式捕捉exceptions

同函数传递参数一样,为了效率和正确性,还是以引用传递比较好。

Catch exceptions by reference !

十四、明智运用exception specifications

所谓exception specifications就是在函数后面加上要抛出的异常类型。如下:

void f2() throw(int);		//保证只抛出int类型的异常
void f3() throw(); //保证不抛出任何异常

exception specifications也会带来问题,当抛出一个未列于其exception specificationexception,这个错误会在运行时检验出来,于是特殊函数unexpeted会被自动调用,终止程序。

void f1();		//f1并没有进行exception specification
void f2() throw(int)
{
//....
f1(); //调用f1(),可能抛出各种异常
//....
}

解决方法是如果被调用的函数没有exception specifications,那么调用函数也不要加exception specifications

同时,在函数指针中调用函数时,也会进行这种检验,如实现一些注册的回调函数时。

第二个问题就是避免将exception specifications放在“需要类型自变量”的template身上。因为template必然以某种方式使用其类型参数,所以不应该templateexception specifications混合使用。

第三个问题是在处理“系统”可能抛出的exceptions。如在使用new operator时,可能抛出bad_alloc异常。

然而,如果你有一些函数都写了exception specifications,那么想要调用其它库中没有exception specifications的函数,我们只能主动将这些函数中抛出的其它异常,转换成UnexceptedException异常。

class UnexceptedException{};
void convertUnexcepted()
{
throw UnexceptedException();
}
set_unexpected(convertUnexpected);

但需要在exception specification中加入UnexceptedException,否则terminate还是被调用。

还有一种做法是将exceptions转换为一个标准类型bad_exception,它的实现依赖于:如果非预期函数的替代者重新抛出当前的(current)exception,它会被标准类型bad_exception代替。所以在exception specifications加入bad_exception即可:

void convertUnexcepted(){
throw; //重新抛出当前exception
}
set_unexpected(convertUnexpected);

exception specifications是一把双面刃,在“函数希望抛出什么样的exception”方面给出了卓越的说明,但同时在“违反exception specifications”时结局也比较悲惨。

十五、了解异常处理(exception handling)的成本

为了支持exception,程序必须做大量簿记工作。“天下没有免费的午餐”就是这个道理。必须在每一个try语句块的进入点和离开点做记号,针对每个try语句,必须记录对应的catch子句能处理的exceptions类型。

使用try语句块代码大约膨胀5%~10%, 执行速度也将下降。

如果有性能上的问题,利用分析工具(profiler)分析程序性能的瓶颈问题。

###《More Effective C++》- 异常的更多相关文章

  1. Java异常(二) 《Effective Java》中关于异常处理的几条建议

    概要 本章是从<Effective Java>摘录整理出来的关于异常处理的几条建议.内容包括:第1条: 只针对不正常的情况才使用异常第2条: 对于可恢复的条件使用被检查的异常,对于程序错误 ...

  2. Effective C++ 条款08:别让异常逃离析构函数

    1.别让异常逃离析构函数的原因 <Effective C++>第三版中条款08建议不要在析构函数中抛出异常,原因是C++异常机制不能同时处理两个或两个以上的异常.多个异常同时存在的情况下, ...

  3. 《More Effective C++ 》读书笔记(二)Exception 异常

    这事篇读书笔记,只记录自己的理解和总结,一般情况不对其举例子具体说明,因为那正是书本身做的事情,我的笔记作为梳理和复习之用,划重点.我推荐学C++的人都好好读一遍Effective C++ 系列,真是 ...

  4. Effective java笔记(八),异常

    57.只针对异常的情况才使用异常 try { int i = 0; while(true) range[i++].climb(); }catch(ArrayIndexOutOfBoundsExcept ...

  5. Effective Java 读书笔记之八 异常

    一.只针对异常的情况才使用异常 1.类具有状态相关的方法时,可采用状态测试方法和可识别的返回值两个策略. 二.对可恢复的情况使用受检异常,对编程错误使用运行时异常 1.期望调用者能够适当恢复的情况,应 ...

  6. Effective C++ -----条款29:为“异常安全”而努力是值得的

    异常安全函数(Exception-safe functions)即使发生异常也不会泄露资源或允许任何数据结构败坏.这样的函数区分为三种可能的保证:基本型.强烈型.不抛异常型. “强烈保证”往往能够以c ...

  7. Effective C++ -----条款08: 别让异常逃离析构函数

    析构函数绝对不要吐出异常.如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序. 如果客户需要对某个操作函数运行期间抛出的异常作出反应,那么class应 ...

  8. [Effective Java]第九章 异常

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  9. Effective C++ Item 29 为”异常安全”而努力是值得的

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:异常安全函数即使发生异常也不会泄漏资源或同意不论什么数据结构败坏.这种函数区分为三种 ...

随机推荐

  1. [OC Foundation框架 - 7] NSArray的创建与遍历

    NSArray是不可变的,不能先创建再添加元素 NSArray可以放入任何OC对象,但不能放入基本数据类型.结构体.枚举等非OC对象 不能存储nil   A.常用方法1 创建 返回用量 是否含有某元素 ...

  2. java IO选择流的原则及其与IO流相关类的关系

    1 按照用途进行分类 1.1 按照数据的来源(去向)分类 是文件:FileInputStream, FileOutputStream, FileReader, FileWriter 是byte[]:B ...

  3. VS2015开发环境配置

    1.安装VS2015 Professional(专业版),按需勾选必要项(VC.C#.WEB.GIT) Visual Basic 2015 00322-50050-03552-AA642Microso ...

  4. HTML5要点(一)

    一.标签: <!-- -->:注释:cmd +/ <!DOCTYPE html><!--HTML5 专属版本声明标签(表明该页面使用HTML编写),代码最上层--> ...

  5. Unity3d 使用NPOI读写Excel 遇到的问题

    开发环境:unity5.3  NPOI(.net 2.0版  http://npoi.codeplex.com/) 运行环境:PC版, 其他平台没有测试 先上效果图: 实现步骤: 1.新建一个Exce ...

  6. 放弃移动版Flash而非AIR

    之前看到标题为"Adobe放弃移动版flash"的新闻,我很震惊,为何Adobe会放弃这么一个大市场呢? 这样无疑打击原来在flash的开发上的应用,我想很多人和我想的一样,fla ...

  7. 【转】如何从零开始学会 Ruby on Rails?

    文章转自:http://huacnlee.com/blog/how-to-start-learning-ruby-on-rails/ 这个话题曾经给身边的很多朋友说过同样的话题,这里整理以下. 如果你 ...

  8. 汉语转拼音pinyin4j

    分享一个将汉语转成拼音的工具包:pinyin4j-2.5.0.jar,下载地址:http://download.csdn.net/detail/abc_key/7629141 使用例如以下代码 imp ...

  9. C_数据结构_链表的链式实现

    传统的链表不能实现数据和链表的分离,一旦数据改变则链表就不能用了,就要重新开发. 如上说示:外层是Teacher,里面小的是node. #ifndef _MYLINKLIST_H_ #define _ ...

  10. 根据ip地址从第三方接口获取详细的地理位置

    最近项目某个功能需要根据ip地址从第三方接口获取详细的地理位置,从网上找了很多例子,主要接口有新浪的,淘宝的,腾讯的.试了淘宝的,如果是数量级小的还可以,如果数量级达到上十万级就速度慢了,会导致系统崩 ...