почему бросок "ничто" вызывает завершение программы? - PullRequest
14 голосов
/ 16 марта 2009
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). Использование этой концепции в любом другом месте приведет к завершению программы, тогда почему компилятор не поднимает флаги во время компиляции?

Ответы [ 7 ]

28 голосов
/ 16 марта 2009

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

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

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

12 голосов
/ 16 марта 2009

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

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

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

10 голосов
/ 16 марта 2009

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

15.1 Бросок исключения

...

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

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

0 голосов
/ 16 марта 2009

Чтобы завершить предыдущие ответы с примером, когда / почему компилятор не может обнаружить проблему:

// 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 компилятор не может знать, как и когда она будет вызвана.

0 голосов
/ 16 марта 2009

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

Бросок вне блока catch без аргументов вызовет завершение программы.

0 голосов
/ 16 марта 2009

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

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

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

0 голосов
/ 16 марта 2009

Вам нечего ловить, и поэтому исключение пузырится на всем протяжении. Даже catch(...) нужно что-то .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...