代码之家  ›  专栏  ›  技术社区  ›  Brian R. Bondy

是否在捕获异常后确定异常类型?

  •  34
  • Brian R. Bondy  · 技术社区  · 16 年前

    有没有一种方法可以确定异常类型,甚至知道您用catch all捕获了异常?

    例子:

    try
    {
       SomeBigFunction();
    }
    catch(...)
    {
       //Determine exception type here
    }
    
    9 回复  |  直到 6 年前
        1
  •  23
  •   anon    16 年前

    实际上,您可以确定catch(…)内的类型,但它不是很有用:

    #include <iostream>
    #include <exception>
    
        class E1 : public std::exception {};
        class E2 : public std::exception {};
    
        int main() {
            try {
                throw E2();
            }
            catch( ... ) {
                try {
                    throw;
                }
                catch( const E1 & e ) {
                    std::cout << "E1\n";
                }
                catch( const E2 & e ) {
                    std::cout << "E2\n";
                }
            }
        }
    
        2
  •  28
  •   Loki Astari    16 年前

    简短回答:不。

    长回答:

    如果您从一个公共基类型(比如std::exception)派生所有异常并显式捕获它,那么您可以使用它从异常中获取类型信息。

    但您应该使用catch的特性将catch作为特定类型的异常进行捕获,然后从中进行工作。

    catch(…)的唯一真正用途是:

    • 捕获:并丢弃异常(停止异常转义析构函数)。
    • 捕获:记录发生的未知异常并重新引发。

    编辑: 您可以通过dynamic_cast<gt;()或通过typid()提取类型信息。 尽管如上所述,我不建议这样做。使用案例陈述。

    #include <stdexcept>
    #include <iostream>
    
    class X: public std::runtime_error  // I use runtime_error a lot
    {                                   // its derived from std::exception
        public:                         // And has an implementation of what()
            X(std::string const& msg):
                runtime_error(msg)
            {}
    };
    
    int main()
    {
        try
        {
            throw X("Test");
        }
        catch(std::exception const& e)
        {
            std::cout << "Message: " << e.what() << "\n";
    
            /*
             * Note this is platform/compiler specific
             * Your milage may very
             */
            std::cout << "Type:    " << typeid(e).name() << "\n";
        }
    }
    
        3
  •  7
  •   user697683    7 年前

    没有标准的、可移植的方法来做到这一点。这里有一个在GCC和Clang上实现它的不可移植方法

    #include <iostream>
    #include <cxxabi.h>
    
    const char* currentExceptionTypeName()
    {
        int status;
        return abi::__cxa_demangle(abi::__cxa_current_exception_type()->name(), 0, 0, &status);
    }
    
    int main()
    {
        try {
            throw std::string();
        } catch (...) {
            std::cout<<"Type of caught exception is "<<currentExceptionTypeName()<<std::endl;
        }
    
        return 0;
    }
    

    输出:

    Type of caught exception is std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
    
        4
  •  6
  •   Harper Shelby damiankolasa    16 年前

    如果您需要根据异常的性质以不同的方式处理异常,那么应该捕获特定的异常。如果有一组异常都需要以相同的方式处理,那么从一个公共的基类派生它们并捕获基类将是一种可行的方法。利用语言的力量和范例,不要与之抗争!

        5
  •  1
  •   JaredPar    16 年前

    不。

    这样做至少需要您能够访问当前的异常。我不相信有一种标准的方法可以做到这一点。

    一旦有了异常实例,就必须使用类型检查算法。C++对此没有内在的支持。充其量,您必须有一个带有动态类型转换的大型if/elseif语句来检查类型。

        6
  •  0
  •   siposa    9 年前

    我尝试过各种方法;这对我很有用:

    从子类化开始 运行时错误 :

    /*----------------------------------------------------------------------*/    
    /* subclass runtime_error for safe exceptions in try/throw/catch        */
    
     #include <stdexcept>
    /* a little preprocessor magic here -- makes a subclass of runtime_error*/
    
    #define NEWERROR( NE )  class NE  : public runtime_error {              \
            public:  NE ( string const& error ) : runtime_error(error) {}  }
    
    
    NEWERROR( FileError      );
    NEWERROR( NetworkError   );
    NEWERROR( StringError    );
    NEWERROR( CofeeError     );
    
    /*----------------------------------------------------------------------*/
    

    然后您可以创建一些例外的实例。

    /*----------------------------------------------------------------------*/
    /* some example pre-defined exceptions  */
    
    FileError     ReadOnly                ( "ReadOnly"             );
    FileError     FileNotFound            ( "FileNotFound"         );
    NetworkError  TimeOutExceeded         ( "TimeOutExceeded"      );
    NetworkError  HostNotFound            ( "HostNotFound"         );
    CoffeeError   OutOfCoffee             ( "OutOfCoffee"          );
    
    /*----------------------------------------------------------------------*/
    

    显式通知编译器您的函数可能引发异常 或者程序可能会在抛出点终止,数据可能丢失或损坏。 如果当时正在使用资源。

    “确保你能抓住任何你能扔的东西。”

    (我使用通用的 运行时错误 因为投掷和接球覆盖了所有 我的异常加上系统的异常。)

    /*----------------------------------------------------------------------*/
    /* example function that may throw an exception */
    
    #include <fstream>
    
    ifstream& getFileStream (string fname) throw (runtime_error)
     {
    
        if ( fname == "" ) 
          throw StringError( "<getFileStream> fname:empty string" );
          // processing stops here if thrown
    
        try 
          {
           ifstream Inputfstream;  
    
           ifstream& ifsref = Inputfstream;
    
           // ifstream has its own <legacy> exception
           // mechanisms and procedures 
           ifsref.exceptions ( ifstream::failbit | ifstream::badbit );
    
           ifsref.open (fname , ifstream::in);  // could fail ==> ifstream::failure exception
          }
        catch (ifstream::failure e) 
          {
           throw FileError( fname + string(e.what() ) ); 
          }
    
        return ifsref;
     }
    
    /*----------------------------------------------------------------------*/
    

    然后在你的尝试/捕获中

    /*----------------------------------------------------------------------*/
    catch (FileNotFound fnf) //catch a specific error
     {
      if (DEBUG) cerr << "[File Not Found Error: " << fnf.what() << "]" << endl;
      ... (handle it) ... 
     }  
    catch (FileError fe) //catch a specific type
     {
      if (DEBUG) cerr << "[File Error: " << fe.what() << "]" << endl;
      ... (handle it) ... 
     }  
    catch (runtime_error re ) // catch a generic type
     {
       if (DEBUG) cerr << "[Runtime error: " << re.what() << "]" << endl;          
    
        // determine type by string comparison
       if ( re.what() == string("ResourceNotavailable") )  ...
       if ( re.what() == string("NetWorkError")         )  ...   
    
      ...
    
    }
    catch ( ... )  // catch everything else 
     { ... exit, rethrow, or ignore ... }
    
    /*----------------------------------------------------------------------*/
    

    这个 运行时错误 类在C++标准库中有很好的支持, 编译器在内部了解它,以及如何优化内存和调度, 因此,您可以在不同的代码基础上安全、自信地使用它们。该代码是可移植的,并且与许多不同的编译器和体系结构兼容。

    如果您觉得一系列的字符串匹配严重浪费了CPU和内存,那么最好在catch子句中单独捕获每个错误,从更具体的到更通用的,速度也更快(不过编译器会对这些进行优化)。

    <stdexcept> 在两个组中为您提供几种例外:

    • 逻辑错误:

      logic_error
      domain_error
      invalid_argument
      length_error
      out_of_range
      
    • 运行时错误:

      runtime_error
      range_error
      overflow_error
      underflow_error
      

    其中一些用法语法略有不同。

    C++中的传统智慧说,你的异常应该是相对“平坦”的, 这意味着,应避免出现特定类别例外的大型层次结构。 对于一般的编程任务,倾向于使用短的通用但信息丰富的代码。特定于领域的任务,如网络系统逻辑、高等数学等,可能受益于特定性,但可以通过生成带有一般运行时/逻辑异常的智能错误字符串轻松实现。

    最后, 我的观点是 :你可以通过 仅引发和捕获运行时\错误 .

    您不必创建一个包含高度特定异常的完整技巧包。 (像Java一样)对于每个类,每个都处理一个特定的错误。

        7
  •  0
  •   Andrew Falanga    7 年前

    这个问题是一段时间前被问到的,我提供这个答案作为9年前被接受的答案的补充。我必须同意被告的回答,“…“不是很有用。”此外,它打开了一个异常的门,这个异常曾被处理过,但未经处理。举例来说,让我以被告的回答为基础

    #include <iostream>
    #include <exception>
    
    class E1 : public std::exception {};
    class E2 : public std::exception {};
    class E3 : public std::exception {};
    
    int main() {
        try {
            throw E3();
        }
        catch( ... ) {
            try {
                // OOOPS!!! E3 is now unhandled!!!!!!
                throw;
            }
            catch( const E1 & e ) {
                std::cout << "E1\n";
            }
            catch( const E2 & e ) {
                std::cout << "E2\n";
            }
        }
    }
    

    这种方法的另一种选择是:

    #include <iostream>
    #include <exception>
    
    class E1 : public std::exception {};
    class E2 : public std::exception {};
    class E3 : public std::exception {};
    
    int main() {
        try {
            throw E3();
        }
        catch( const E1 & e ) {
            std::cout << "E1\n";
        }
        catch( const E2 & e ) {
            std::cout << "E2\n";
        }
        catch( ... ) {
            std::cout << "Catch-all...";
        }
    }
    

    第二种方法似乎等同于第一种方法,并且具有特殊处理的优势。 E1 E2 然后抓住其他一切。仅作为备选方案提供。

    请注意,根据2011-02-28的C++草案,第15.3段,子弹项5, 如果有,A…handler应该是其try块的最后一个处理程序。

        8
  •  0
  •   user8315449    6 年前

    如果C++ 11可用,

    bool throwing_func()
    {
        // something is wrong...
        throw char('5');
    
        // ...
    
        return true;
    }
    
    void exception_handler(std::exception_ptr _Eptr)
    {
        try 
        {
            if (_Eptr) {std::rethrow_exception(_Eptr);}
        }
    
    
        catch(int _Xi)
        {
            std::cout << "int\n";
        }
    
        catch(char _Xc)
        {
            std::cout << "char\n";
        }
    
        catch(const std::exception& _Xe)
        {
           std::cout << "std::exception " << _Xe.what() << "\n";
        }
    
        catch (...)
        {// develop more catch cases above to avoid what follows
            std::cout << "unhandled exception\n";
            // grande problema
        }
    }
    
    int main()
    {
    
        try
        {
            throwing_func();
        }
    
        catch(...)
        {
            //Determine exception type here
    
             exception_handler(std::current_exception());
        }
    
        return 0;
    }
    
        9
  •  -2
  •   Roderick    14 年前

    如果使用VisualC++(托管),可以使用GETType()方法来获得异常类型并从中处理它。

    例如。

    try
        {
            // Run the application
            Application::Run(mainForm);
        }
        catch (Exception^ e)
        {
            String^ exception_type = e->GetType()->ToString();
            throw;
        }
    

    字符串将包含类似“System.ArgumentOutOfRangeException”的内容。