为什么抛出“无”会导致程序终止?

时间:2009-03-16 16:29:30

标签: c++ exception-handling

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 被错过了。看起来无辜的代码使应用程序崩溃了。

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

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

7 个答案:

答案 0 :(得分:28)

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

  

如果目前没有例外   处理,执行throw-expression   没有操作数调用   终止()(15.5.1)。

至于为什么编译器无法诊断这一点,需要进行一些相当复杂的流程分析,我想编译器编写者不会认为它具有成本效益。 C ++(和其他语言)充满了可能的错误,理论上 可以被编译器捕获但实际上并非如此。

答案 1 :(得分:12)

详细说明尼尔的回答:

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

关于你的下一个问题,为什么编译器不会在catch块之外用throw;发出警告,编译器无法在编译时告诉throw;行是否正在执行在catch块的上下文中。考虑:

// 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中发生异常,则将调用恢复代码。如果该恢复代码成功,则会忘记原始异常并重新尝试该任务。如果恢复代码失败,则忽略该故障并重新抛出先前的故障。这是有效的,因为throw;中的attempt_recovery是在do_something的catch块的上下文中调用的。

答案 2 :(得分:10)

来自C ++标准:

  

15.1抛出异常

     

...

     

如果目前没有例外   处理,执行throw-exception   没有操作数调用terminate()

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

答案 3 :(得分:0)

  

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

Rethrowing非常有用。假设您有三个级别的调用堆栈,每个级别为最终调用添加一些上下文资源对象。现在,当您在叶级别有异常时,您将期望对该对象创建的任何资源进行一些清理操作。但这不是全部,叶子上方的调用者也可能已经分配了一些需要解除分配的资源。你是怎样做的?你重新抛出

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

答案 4 :(得分:0)

在没有args的catch块内部抛出将重新抛出捕获的相同异常,因此它将被捕获到更高级别。

在没有args的catch块之外抛出会导致程序终止。

答案 5 :(得分:0)

使用编译器无法检测到问题的时间/原因的示例来完成先前的答案:

// 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 时,编译器无法知道它将如何以及何时被调用。

答案 6 :(得分:-1)

你没有任何东西可以抓住,所以异常一直在冒泡。即使catch(...)需要某事