【问题标题】:why does throw "nothing" causes program termination?为什么抛出“无”会导致程序终止?
【发布时间】:2009-03-16 16:29:30
【问题描述】:
const int MIN_NUMBER = 4;
class Temp
{
public:

    Temp(int x) : X(x)
    {
    }

    bool getX() const
    {
        try
        {
            if( X < MIN_NUMBER)
            {
                //By mistake throwing any specific exception was missed out
                //Program terminated here
                throw ;
            }
        }
        catch (bool bTemp)
        {
            cout<<"catch(bool) exception";

        }
        catch(...)
        {
            cout<<"catch... exception";
        }
        return X;
    }

private:
    int X;
};



int main(int argc, char* argv[])
{
    Temp *pTemp = NULL;
    try
    {
        pTemp = new Temp(3);
        int nX = pTemp->getX();
        delete pTemp;
    }
    catch(...)
    {
        cout<<"cought exception";
    }

    cout<<"success";
    return 0;
}

在上面的代码中,throw false 是在 getX() 方法中使用的,但由于人为错误(!)false 被遗漏了。看似无辜的代码使应用程序崩溃。

我的问题是为什么当我们抛出“nothing”时程序会终止?

我不太了解throw; 基本上是“重新抛出”,必须在异常处理程序(catch)中使用。在任何其他地方使用这个概念都会导致程序终止,那么为什么编译器在编译期间不引发标志?

【问题讨论】:

    标签: c++ exception-handling


    【解决方案1】:

    这是预期的行为。来自 C++ 标准:

    如果当前没有异常 处理,执行一个抛出表达式 没有操作数调用 终止()(15.5.1)。

    至于为什么编译器无法诊断这一点,需要一些非常复杂的流程分析才能做到这一点,我猜编译器的作者不会认为它具有成本效益。 C++(和其他语言)充满了可能的错误,可以在理论上被编译器捕获,但实际上不是。

    【讨论】:

    • 这并没有回答 aJ 关于为什么“投掷”的另一个问题首先是有用的
    • 我想知道为什么编译器无法检测到这一点?
    • 我猜编译器没有检测到它,因为它是 C++ 规范的一部分。是的,这很糟糕——不,你不应该这样做。那是你的 C++ - 恨它或爱它 :)
    • 编译器无法检测到,因为它涉及运行时行为。
    • aJ,没关系。人们可以从 catch 块中调用该函数,然后 throw ;有效。
    【解决方案2】:

    详细说明尼尔的回答:

    throw; 本身将尝试重新引发当前正在展开的异常——如果正在展开多个异常,它会尝试重新抛出最近的异常。如果没有展开,则调用 terminate() 以表明您的程序做了一些虚假的事情。

    至于您的下一个问题,为什么编译器不会在 catch 块之外使用 throw; 发出警告,是因为编译器无法在编译时判断 throw; 行是否可能在上下文中执行一个捕获块。考虑:

    // you can try executing this code on [http://codepad.org/pZv9VgiX][1]
    #include <iostream>
    using namespace std;
    
    void f() {
        throw 1;
    }
    void g() {
        // will look at int and char exceptions
        try { 
            throw;
        } catch (int xyz){
            cout << "caught int " << xyz << "\n";
        } catch (char xyz){
            cout << "caught char " << xyz << "\n";
        }
    }
    void h() {
        try {
            f();
        } catch (...) {
            // use g as a common exception filter
            g();
        }
    }
    int main(){
        try {
            h();
        } catch (...) {
            cout << "some other exception.\n";
        }
    }
    

    在这个程序中,g() 用作异常过滤器,可以从h() 和任何其他可以使用此异常处理行为的函数中使用。你甚至可以想象更复杂的情况:

    void attempt_recovery() {
        try{
            // do stuff
            return;
    
        } catch (...) {}
    
        // throw original exception cause
        throw;
    }
    void do_something() {
        for(;;) {
            try {
                // do stuff
            } catch (...) {
                attempt_recovery();
            }
        }
    }
    

    这里,如果do_something发生异常,就会调用恢复代码。如果该恢复代码成功,则忘记原始异常并重新尝试该任务。如果恢复代码失败,则忽略该失败并重新抛出先前的失败。这是因为attempt_recovery 中的throw; 是在do_something 的catch 块的上下文中调用的。

    【讨论】:

      【解决方案3】:

      来自 C++ 标准:

      15.1 抛出异常

      ...

      如果当前没有异常 处理,执行抛出异常 没有操作数调用terminate()

      编译器无法可靠捕获此类错误的原因是异常处理程序可以调用函数/方法,因此编译器无法知道throw 是否发生在catch 中。这本质上是运行时的事情。

      【讨论】:

        【解决方案4】:

        我不太了解那个扔;基本上是“重新抛出”,必须在异常处理程序(catch)中使用。在任何其他地方使用这个概念都会导致程序终止,那么为什么编译器在编译期间不引发标志?

        重新投掷很有用。假设您有一个三层深的调用堆栈,每一层都为最终调用添加一些上下文资源对象。现在,当您在叶级别遇到异常时,您将期望对对象创建的任何资源进行一些清理操作。但这还不是全部,叶子上方的调用者也可能分配了一些需要释放的资源。你是怎样做的?你再扔

        但是,您所拥有的不是重新抛出。这是在尝试捕获和处理引发的任何和所有异常失败后放弃的信号。

        【讨论】:

          【解决方案5】:

          在不带参数的 catch 块内的 throw 将重新抛出被捕获的相同异常,因此它将在更高级别被捕获。

          在没有参数的 catch 块之外抛出将导致程序终止。

          【讨论】:

            【解决方案6】:

            以编译器何时/为什么无法检测到问题的示例来完成前面的答案:

            // Centralized exception processing (if it makes sense)
            void processException()
            {
               try {
                  throw;
               }
               catch ( std::exception const & e )
               {
                  std::cout << "Caught std::exception: " << e.what() << std::endl;
               }
               catch ( ... )
               {
                  std::cout << "Caught unknown exception" << std::endl;
               }
            }
            
            int main()
            {
               try
               {
                  throw 1;
               }
               catch (...)
               {
                  processException(); // correct, still in the catch clause
               }
               processException(); // terminate() no alive exception at the time of throw.
            }
            

            编译函数processException时,编译器不知道如何以及何时调用它。

            【讨论】:

              【解决方案7】:

              您没有任何要捕获的内容,因此异常会一直冒泡。甚至catch(...) 也需要一些东西

              【讨论】:

              • 我想他在问为什么“扔”;在 catch 块之外是合法的,而不是为什么它没有被抓住。
              • @jalf:不,我想他是在问为什么它没有被抓住。 Neil Butterworth 的回答解决了这个问题。
              • @aJ:编译器此时无法知道。您可以创建一个函数来重新抛出捕获的最后一个异常并对其进行处理(我们这样做是为了集中一些 CORBA 异常)。编译该函数时,编译器不会知道调用者是否真的捕获了某些东西。
              • 他问了这两个问题 - 为什么它没有被抓住?为什么编译器不抱怨重新抛出不在catch中?
              猜你喜欢
              • 1970-01-01
              • 2011-02-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-01-08
              • 1970-01-01
              相关资源
              最近更新 更多