zoukankan      html  css  js  c++  java
  • ###《More Effective C++》- 异常

    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)分析程序性能的瓶颈问题。

  • 相关阅读:
    C++并发编程实战---阅读笔记
    设计模式---命令模式
    图解HTTP(六)HTTP首部
    HTTP 状态码
    使用VS2012调试Dump文件
    如何设置C++崩溃时生成Dump文件
    boost::asio::io_service类
    boost::asio 同步&异步例子
    boost::bind
    c++并发编程之原子操作的实现原理
  • 原文地址:https://www.cnblogs.com/gr-nick/p/4583803.html
Copyright © 2011-2022 走看看