Почему бросок "ничего" вызывает завершение программы?

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; - это в основном "rethrow" и должен использоваться в обработчике исключений (catch). Использование этой концепции в любом другом месте приведет к расторжению программы, то почему компилятор не создает флаги во время компиляции?

Ответы

Ответ 1

Это ожидаемое поведение. Из стандарта С++:

Если в настоящее время исключение отсутствует обрабатывается, выполняя выражение throw без вызовов операнда прекратить() (15.5.1).

Что касается того, почему компилятор не может диагностировать это, для этого потребуется какой-то довольно сложный анализ потока, и я думаю, что авторы компилятора не считают это рентабельным. С++ (и другие языки) полны возможных ошибок, которые теоретически могут быть пойманы компилятором, но на практике это не так.

Ответ 2

Чтобы уточнить ответ Нила:

throw; сам по себе попытается повторно поднять текущее исключение, которое будет разворачиваться - если несколько разматываются, он пытается восстановить последнюю. Если ни один не разматывается, то terminate() вызывается, чтобы сигнализировать, что ваша программа сделала что-то фиктивное.

Что касается вашего следующего вопроса, почему компилятор не предупреждает с throw; за пределами блока catch, заключается в том, что компилятор не может сказать во время компиляции, может ли строка 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 вызывается в контексте блока catch do_something.

Ответ 3

Из стандарта С++:

15.1 Отбрасывание исключения

...

Если в настоящее время исключение отсутствует обрабатывается, выполняя исключение throw-exception без вызовов операндов terminate()

Причина, по которой компилятор не может надежно поймать этот тип ошибки, заключается в том, что обработчики исключений могут вызывать функции/методы, поэтому компилятору не удастся узнать, происходит ли throw внутри catch. Это, по существу, среда выполнения.

Ответ 4

У меня мало понимания этого броска; в основном "rethrow" и должен использоваться в обработчике исключений (catch). Использование этой концепции в любом другом месте приведет к расторжению программы, то почему компилятор не создает флаги во время компиляции?

Повторное использование полезно. Предположим, что у вас есть стек вызовов на три уровня с каждым уровнем, добавляющим некоторый объект контекстного ресурса для окончательного вызова. Теперь, когда у вас есть исключение на уровне листа, вы ожидаете некоторую операцию очистки для любых ресурсов, которые создал объект. Но это еще не все, вызывающие выше листа могут также выделить некоторые ресурсы, которые необходимо будет освободить. Как ты это делаешь? Ты ревертировал.

Однако у вас есть не ретрон. Это сигнал отказа от некоторых неудачных попыток поймать и обработать все и все исключения, которые были подняты.

Ответ 5

Бросок внутри блока catch без аргументов будет перебрасывать одно и то же исключение, которое было поймано, поэтому оно будет поймано на более высоком уровне.

Бросок за пределами блока 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(...) нуждается в чем-то.