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. hdu 5504 GT and sequence

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5504 GT and sequence Time Limit: 2000/1000 MS (Java/O ...

  2. 浅谈iOS中MVVM的架构设计与团队协作

    说到架构设计和团队协作,这个对App的开发还是比较重要的.即使作为一个专业的搬砖者,前提是你这砖搬完放在哪?不只是Code有框架,其他的东西都是有框架的,比如桥梁等等神马的~在这儿就不往外扯了.一个好 ...

  3. 【UML】——为什么要使用UML

    以前一提到UML,就想到了复杂的流程图.很敬佩哪些想想就能画出整个系统的UML图的人,因为他们头脑中有整个软件架构的蓝图,这样在编写实现的时候,就会知道哪个地方改怎么做,哪个地方如何扩展. 而想成为架 ...

  4. XML文件

    XML 指可扩展标记语言(eXtensible Markup Language) XML 被设计用来传输和存储数据. 什么是 XML? XML 指可扩展标记语言 XML 是一种标记语言,非常类似 HT ...

  5. js大小写转换

    toUpperCase 方法返回一个字符串,该字符串中的所有字母都被转化为大写字母 sn.toUpperCase() toLowerCase 方法返回一个字符串,该字符串中的字母被转换为小写字母 sn ...

  6. Android系统Google Maps开发实例浅析

    Google Map(谷歌地图)是Google公司提供的电子地图服务.包括了三种视图:矢量地图.卫星图片.地形地图.对于Android系统来说,可以利用Google提供的地图服务来开发自己的一些应用. ...

  7. Android Touch事件原理加实例分析

    Android中有各种各样的事件,以响应用户的操作.这些事件可以分为按键事件和触屏事件.而Touch事件是触屏事件的基础事件,在进行Android开发时经常会用到,所以非常有必要深入理解它的原理机制. ...

  8. MyEclipse设置默认的目光格式

    首先,选择菜单 windows-->preference Java-->Code Style-->Code Templates code-->new Java files 然后 ...

  9. 在centos上使用yum安装redis及php扩展php-redis

    .wget http://mirrors.ustc.edu.cn/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm rpm -ivh epel-rele ...

  10. 几种sap增强的查找方法

    ***方法一**************************************** 通过SE30,运行TCODE后,点Evaluate后,查看运行时间分析评估:命中清单. 找以“exit”开 ...