• C++的异常处理之一:throw是个一无是处的东西


    这篇文章学习C++异常处理的基础知识。看完后,还不过瘾,为什么大家在C++代码中都不用Exception?为什么C++11会引入一些变化?

    为什么C++ exception handling需要unwind stack?

    为什么throw会被抛弃?

    接着看http://www.gotw.ca/publications/mill22.htm, 总结如下:

    throw() specification 可以让程序员在自己写的函数上标注该函数是否会抛出异常, 会抛出哪些类型的异常,但是throw有如下的问题:

    1. C++基于throw的异常处理部分是一个“Shadow Type System”

    为什么这么说呢?throw()语法有时被认为是函数签名的一部分,而有的时候却不允许,而这没有标准规定,比如下面的例子

    // Example 2(a): You can’t write an ES
    // in a typedef.
    //
    void f() throw(A,B);
    
    typedef void (*PF)() throw(A,B); // syntax error 
    
    PF pf = f;                       // can’t get here
    

    在typdef时不把throw作为函数类型的一部分,但看下面的例子:

    // Example 2(b): But you can if you omit
    // the typedef!
    //
    void f() throw(A,B);
    void (*pf)() throw(A,B);   // ok
    pf = f;                    // ok
    

    还有函数指针的例子:

    // Example 2(c): Also kosher, low-carb,
    // and fat-free.
    //
    void f() throw(A,B);
    void (*pf)() throw(A,B,C); // ok
    pf = f;                    // ok, less restrictive
    

    还有继承的virtual 函数的例子:

    // Example 2(d): And the ES in the signature
    // does matter if it’s a virtual function.
    //
    class C
    {
      virtual void f() throw(A,B); // same ES
    };
    
    class D : C
    {
      void f(); // error, now the ES matters
    };
    

      

      

    2. 对throw语法的(错误)理解

    很多人认为throw表示下面的意思:

    1. Guarantee that functions will only throw listed exceptions (possibly none).
    2. Enable compiler optimizations based on the knowledge that only listed exceptions (possibly none) will be thrown.

    对第一条,看下面的代码:

    // Example 1(b) reprise, and two
    // potential white lies:
    //
    int Gunc() throw();    // will throw nothing (?)
    
    int Hunc() throw(A,B); // can only throw A or B (?)
    

    Gunc()真的不会throw任何异常吗?Hunc()真的只会抛出类型A和B的异常吗?不是的,代码复杂、调用嵌套多次后,很多时候程序员是无法准确标注这个函数会throw什么样的exception。而一旦未被标注的异常发生后,编译器也只是默默地做点事情,而这对我们的程序没有什么帮助。如果一个未被标准的异常发生后,编译器就调用std::unexpected()函数。unexpected()函数是全局的,很难对特定的exception提供很有帮助的处理,大部分情况就直接terminate,而且unexpected()函数是不会返回的,所以,这样的异常一旦发生,就等于退出程序。

    对throw的理解应该换成下面的两句:

    • Guarantee Enforce at runtime that functions will only throw listed exceptions (possibly none).
    • Enable or prevent compiler optimizations based on the knowledge that only listed exceptions (possibly none) will be thrown having to check whether listed exceptions are indeed being thrown.

    下面看看编译器干了什么,对下面的代码:

    // Example 3(a)
    //
    int Hunc() throw(A,B)
    {
      return Junc();
    }
    

    编译器生成如下代码:

    // Example 3(b): A compiler’s massaged
    // version of Example 3(a)
    //
    int Hunc()
    try
    {
      return Junc();
    }
    catch( A )
    {
      throw;
    }
    catch( B )
    {
      throw;
    }
    catch( ... )
    {
      std::unexpected(); // will not return! but
    }  // might throw an A or a B if you’re lucky
    

    可见,编译器并不是根据listed exception做优化,编译器需要生成更多的代码来保证在运行时只有listed exception被throw出来,而没有list就调用unexpected函数了。

    在回头看对throw的正确的理解:

    1. 保证运行时,只会throw listed exception,而如果发生不listed 的exception,那就调用unexpected;

    2. 允许或者禁止编译器不得不进行是否listed exception发生的检查。  

    在上面说明了使用throw时,编译器需要生成try-catch代码, 其实throw还有其他问题:1. 有些编译器会自动拒绝为使用throw的function做inline优化;2. 有些编译器不回去对与exception有关的知识进行优化,即使有些代码绝不会throw exception,但编译器还是会生成try-catch代码。3. throw在virtual函数中时signature的一部分,所以,如果你把base class 的virtual方法中throw的类型之一或者若干个去掉了,那就也需要更新子类的代码,这样实际上增加了coupling,是很不好的设计。

    所以关于throw,建议是

    Moral #1: Never write an exception specification.

    Moral #2: Except possibly an empty one, but if I were you I’d avoid even that.

    现在大家都做cross-platform的开发,多一事不如少一事,throw不会带来什么好处,所以,就完全不要用了。

    既然throw有如此多的问题,那C++11带来了什么呢?

     

    Reference:

    1. http://www.learncpp.com/cpp-tutorial/153-exceptions-functions-and-stack-unwinding/

    2. http://www.gotw.ca/publications/mill22.htm

    3. http://stackoverflow.com/questions/88573/should-i-use-an-exception-specifier-in-c/88905#88905

    4. http://stackoverflow.com/questions/10787766/when-should-i-really-use-noexcept

  • 相关阅读:
    MVP on dot NET Episode 1
    以服务器端为中心的 ASP.NET AJAX 模式 (Part 2 Control)
    以服务器端为中心的 ASP.NET AJAX 模式 (Part 1 Behavior)
    编写 iPhone Friendly 的 Web 应用程序 (Part 6 iUI)
    Vista 为什么要引入 UAC
    Windows 就是一个带 UI 的命令行
    ASP.NET AJAX 4.0 Preview 3 (Part 2 ASP.NET AJAX Template)
    如何订阅MVP on dot NET(或其它播客) iTunes版
    深入理解 ASP.NET 动态控件 (Part 5 编译实验)
    使用 .NET 实现 Ajax 长连接 (Part 2 Mutex Wait & Signal)
  • 原文地址:https://www.cnblogs.com/whyandinside/p/3677589.html
Copyright © 2020-2023  润新知