设为首页 加入收藏

TOP

C++ 之 exception(一)
2015-07-20 17:30:36 来源: 作者: 【 】 浏览:11
Tags:exception

本文讲关于C++的异常的所有东西:

绝对不让异常逃离析构函数

  • 阻止exception逃离析构函数,主要是两个原因:
    1 防止在异常处理过程中的栈展开行为时,将调用terminate函数。程序将会结束,有时候其实错误并没有那么严重。

    [插入: 什么时候会调用terminate函数呢?]
    [回答 : By default, the terminate handler calls abort. But this behavior can be redefined by calling set_terminate.

    This function is automatically called when no catch handler can be found for a thrown exception, or for some other exceptional circumstance that makes impossible to continue the exception handling process.
    //对于一个异常没有相应的catch语句块的话,就会自动调用terminate函数,或者是对于一些无法处理的异常的情况时。

    This function is provided so that the terminate handler can be explicitly called by a program that needs to abnormally terminate, and works even if set_terminate has not been used to set a custom terminate handler (calling abort in this case).

    terminate被调用的情况:

    1 当发送一个异常,并且构造函数产生异常
    2 当发送一个异常,或者析构函数产生异常
    3 一个静态对象的构造或者析构发送一个异常
    4 以atexit注册的函数发生异常的时候
    5 自定义一个异常,但是实际上没有异常产生的时候
    6 调用缺省的unexcepted()函数时候 (unexcepted函数是因为该函数抛出了没有预期的异常)


    2 可以协助确保destructor完成它应该完成的所有的动作。

    #include 
         
           using namespace std; class myexception{}; class Session { public: Session() { logCreation(); } ~Session() { try{ //这里的try catch块是很重要的 logDestruction(); }catch(...){ cout << "catch exception..." << endl; } } private: static void logCreation(){cout << "enter..." << endl;} static void logDestruction() {cout << "out..." << endl;throw myexception();} }; int main() { Session s; return 0; }
         

    catch的子语句块 VS.函数的调用

    3大类的不同:

    1 exception 对象总是被复制。如果以by value的方式,甚至复制两次。

    2 , 被抛出作为exception的对象,允许的转换操作比“被传递到函数中”的少。

    3 catch子句以其出现的顺序进行匹配,也就是 “first fit”的策略, 不像函数调用的 “best fit”的策略

    • 相同点: 函数参数以及exception的传递方式都有三种: by value, by reference , by pointer,
    • 不同点: 当你调用一个函数的时候,调用完毕之后控制权最终会回到该函数的调用端; 但是当你抛出一个异常的时候,控制权永远都不会回到抛出端。
    • 不同点: 函数的绑定方式,如果是传值,那么传递的是该对象的副本,如果是引用,那么直接就是绑定到该对象。但是,这对于异常是不对的,无论你的异常的catch的参数是传值还是传引用,所有传递给该catch块的都是该对象的副本。(为什么是这样呢?因为一旦控制权离开了该函数,那么该函数的所有的局部对象都离开了生存空间,就会自动析构掉,那么此时传给该catch块的对象便是一个已经析构了的对象,这显然是不对的。即使该临时对象的生存期不仅仅限于该函数,该结论依然成立)。 这里的 “exception对象总是会导致复制“的这一行为,直接导致了异常处理常常比 函数调用慢!!!
    • 当对象复制被当做一个exception的,复制行为是由对象的拷贝构造函数来执行的,这个拷贝构造函数相应于该对象的静态类型!!而不是动态类型!!例如?例如下面的代码:
      class Base{
      public:
          Base(){cout << "ctor in Base..." << endl;}
          Base(const Base &){cout << "copy ctor in Base..." << endl;}
      };
      
      class Derived : public Base {
      public:
          Derived(){cout  << "ctor in Derived..." << endl;}
          Derived(const Derived &b):Base(b){cout << "copy ctor in Derived..." << endl;}
      };
      
      void f()
      {
          Derived d;  // print : ctor in Base  ctor in Derived
          Base &b = d;
          cout << "throw..." << endl;
          throw b;  //copy ctor in Base
      }
      
      void test()
      {
          try{
              f();
              cout << "never be here..." << endl;
          }catch(Base) // 如果这儿是Base,那么print: copy ctor in Base,如果是Base reference,那么不输出任何东西
          {
              cout << "catch..." << endl;
          }
      }
      
      int main()
      {
          test();
          return 0;
      }
      • 由上面知道“exception对象是其他对象的副本”,这里就会出现在不同的catch块中如何传递exception的问题!!!

        下述的throw 和throw w分别来模拟catch块中的继续传递当前exception的操作。

        //利用throw语句继续传递当前的exception,如果直接使用throw,那么传递的是最初传递来的exception的实际类型,(test1中的throw)
        //如果是使用throw + catch形参的方式,那么传递的是静态类型。
        
        class Base{
        public:
            Base(){cout << "ctor in Base..." << endl;}
            Base(const Base &){cout << "copy ctor in Base..." << endl;}
        };
        
        class Derived : public Base {
        public:
            Derived(){cout  << "ctor in Derived..." << endl;}
            Derived(const Derived &b):Base(b) {cout << "copy ctor in Derived..." << endl;}
        };
        
        void f1()
        {
            try{
                Derived d;  // ctor in Base , ctor in Derived
                Base &b = d;
                cout << "throw..." << endl;
                //此处throw的是Derived对象
                throw d;  //copy ctor in Base  , copy ctor in Derived,这里看的是静态类型的!!!
            }catch(Base &w) //Base&是可以捕捉到上述的Derived异常的,无论此处是Base还是Base&,都能捕获到Derived类型的异常。
              //    如果此处是Derived,那么上述的throw如果throw的是b,那么这里不能捕获到。与一般的函数调用的参数绑定方式是一样的。
            {
                cout << "throw current exception 1, using throw only..." << endl;
                throw ; //如果直接使用throw,即使上面的catch是将一个Derived对象绑定到一个Base对象,这里传递的依然是最初的Derived对象。
            }
        }
        
        void test1()
        {
            try{
                f1();
                cout << "never be here..." << endl;
            }catch(Derived)   //copy ctor in Base, copy ctor in Derived
            //}catch(Base) // copy  ctor in Base .
            {
                cout << "catch..." << endl;
            }
        }
        
        void f2()
        {
            try{
                Derived d;  // print : ctor in Base  ctor in Derived
                Base &b = d;
                cout << "throw in f2..." << endl;
                throw b;  //copy ctor in Base //这里的throw传递的则是静态类型。
            }catch(Base &w)
            {
                cout << "throw current exception 2 " << endl;
                throw w; //copy ctor in Base
            }
        }
        
        void test2()
        {
            try{
                f2();
                cout << "never be here in test2..." << endl;
            }catch(Base &) // 如果这儿是Base,那么print: copy ctor in Base,如果是Base reference,那么不输出任何东西
            {
                cout << "catch in test2..." << endl;
            }
        }
        int main()
        {
            cout << "1 ; "<< endl;
            test1();
            cout << "2 : " << endl;
            test2();
            return 0;
        }
        • 不同点: 一个被抛出的对象(根据上述显然是个临时对象)可以以 by reference的方式捕捉,不需要以一个by reference to const的方式捕捉,但是函数调用的过程中将一个临时对象传递给一个non-const-reference参数是不允许的。但是对exception合法。
          [ 将一个临时对象传递给一个non-const-reference参数为什么是不允许的。 ]
          考虑下面的例子:
          void f(const string &s)
          {
              cout << s << endl;
              cout << "const-reference..." << endl;
              return;
          }
          
          void f2(string &s)
          {
              cout << "non-const reference..." << endl;
              return;
          }
          
          int main(void)
          {
              char buff[20] = "1234";
              //f和f2函数需要的是一个string的对象,但是传递的都是buff字符数组,那么这里就会有一个隐式类型转换,生成一个临时的string对象。
              f(buff);   //调用成功。因为是const,不需要改变什么。
              f2(buff); //调用失败,原因:f2需要的是string的non-const reference,也就意味着f2函数可能会修改字符串s,而此时传递给
              //f2的是一个string的临时对象,在函数退出时,该实参没有改变,所有的操作均施加于临时形参s上,显然这并不是程序员的意图,我们传递一个
              //non-const reference给f2,应该就是希望修改buff这个实参的。
              return 0;
          }
          • 不同点: 如果以by value的方式传递一个函数自变量,那么便是对被传递对象做一个副本,此副本存储于对应的函数参数中; 如果以 by value的方式传递exception,会怎么样呢? 例如 :
            catch (Base b) 。。。 //预期得付出 “被抛出物” 的 ”两个副本“的构造代价,一个用于 ”任何exception都会产生的临时对象的“身上,还有一个是 ”将临时对象复制到b”。
          • 如果以by reference的方式的话,那么函数不需要任何额外动作,但是 exception 还是会有一个 “被抛出物”的副本的构造的代价。
          • 如果以by pointer的方式来传递exception的话,那么必须注意的是千万不要跑出一个指向局部对象的指针。这也是 “义务性复制操作”必须考虑的情况。

          • 不同点:throw子句与catch参数的匹配!!
            exception与catch子句的匹配过程仅仅只是两种转换: 1 , 继承架构中的类转换。 (一个针对Base class exception编写的catch子句,可以捕捉到一个derived class的exception) ,注意: 这个转换规则适用于以by value, by reference,或者是by pointer的三种方式。 2、 从一个“有型指针” 转换为一个 “无型指针”,例如一个针对于: const void *设计的catch子句,可以捕捉任意的指针类型的exception。

          • 不同点: catch子句总是依出现的顺序进行匹配。也就是说,针对Derived class exception的catch子句一定要放在针对Base class exception的catch子句的前面。

            Catch exception By reference

            理由:
            + 如果是by pointer的话,那么需要的是一个超出该作用域都不会被销毁的对象,那么此时global和static是可以帮忙的,但是程序员们可能会忘记这些;如果就是在throw的时候才临时

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇leetcode 二分查找 Search for a .. 下一篇Leetcode:merge_sorted_array

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·Linux_百度百科 (2025-12-26 12:51:52)
·Shell 流程控制 | 菜 (2025-12-26 12:51:49)
·TCP/UDP协议_百度百科 (2025-12-26 12:20:11)
·什么是TCP和UDP协议 (2025-12-26 12:20:09)
·TCP和UDP详解 (非常 (2025-12-26 12:20:06)