Выдает ли throw внутри многоточия catch () исходную ошибку в C++?

Если в моем коде у меня есть следующий фрагмент:

try {
  doSomething();
} catch (...) {
  doSomethingElse();
  throw;
}

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


person WilliamKF    schedule 19.03.2010    source источник


Ответы (1)


да. Исключение активно до тех пор, пока оно не будет перехвачено, после чего оно становится неактивным. Но он живет до тех пор, пока область действия обработчика не закончится. Из стандарта, акцент мой:

§15.1/4: память для временной копии создаваемого исключения выделяется неуказанным образом, за исключением случаев, указанных в 3.7.4.1. Временное состояние сохраняется до тех пор, пока для этого исключения выполняется обработчик.

Это:

catch(...)
{ // <--

    /* ... */

} // <--

Между этими стрелками вы можете повторно создать исключение. Только когда область обработчиков заканчивается, исключение перестает существовать.

Фактически, в §15.1/6 приведенный пример почти такой же, как ваш код:

try {
    // ...
}
catch (...) { // catch all exceptions
    // respond (partially) to exception <-- ! :D
    throw; //pass the exception to some
           // other handler
}

Имейте в виду, что если вы throw не активируете исключение, будет вызвано terminate. Это не может иметь место для вас, будучи в обработчике.


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

Это:

void doSomethingElse(void)
{
    try
    {
        throw "this is fine";
    }
    catch(...)
    {
        // the previous exception dies, back to
        // using the original exception
    }

    try
    {
        // rethrow the exception that was
        // active when doSomethingElse was called
        throw; 
    }
    catch (...)
    {
        throw; // and let it go again
    }

    throw "this replaces the old exception";
    // this new one takes over, begins stack unwinding
    // leaves the catch's scope, old exception is done living,
    // and now back to normal exception stuff
}

try
{
    throw "original exception";
}
catch (...)
{
  doSomethingElse();
  throw; // this won't actually be reached,
         // the new exception has begun propagating
}

Конечно, если ничего не выбрасывается, будет достигнуто throw;, и вы сгенерируете пойманное исключение, как и ожидалось.

person GManNickG    schedule 19.03.2010
comment
Что, если doSomethingElse() тем временем выдаст что-то еще? Любопытная вещь. - person John K; 19.03.2010
comment
и обработчик, на котором он был пойман, звучит сбивающе с толку. Согласно процитированному вами абзацу, его закрывающая скобка не требуется. - person Potatoswatter; 19.03.2010
comment
@jdk: terminate вызывается; см. последнюю строку примера, throw 2;, где 2 — новое исключение. - person Mike DeSimone; 19.03.2010
comment
@Potatoswatter: его можно немного переформулировать. Но выполнение обработчика останавливается, когда выполняется последний оператор в области видимости. (Прицел заканчивается.) - person GManNickG; 19.03.2010
comment
активны два исключения, вызывается терминация. Эм, нет. Выброс другого исключения заменяет активное исключение. 15.1/4: когда [обработчик] выходит любым способом, кроме throw;, временный объект уничтожается. В частности, выход с помощью throw выражения-присваивания является другим. Вы думаете о выбросе из деструктора во время раскрутки до выполнения обработчика, и поведение для этого явно определено в 15.2/3. - person Steve Jessop; 19.03.2010
comment
@GMan, @jdk: это звучало неправильно, я не смог найти в стандарте ничего, что бы это поддерживало, поэтому я попробовал поэкспериментировать с GCC, и действительно, создание исключения в блоке catch не вызывает terminate или любого другого другое особое поведение. Согласно §15.3/8, исключение считается обработанным при входе в обработчик. Единственная особенность среды выполнения в блоке throw заключается в том, что на объект исключения ссылается повторный бросок. (И особые случаи, которые применяются к конструкторам/деструкторам.) - person Potatoswatter; 19.03.2010
comment
Пример кода: pastebin.com/GaftqFFr. Обратите внимание, что doSomethingElse выбрасывает (из деструктора, не меньше), но не вызывает терминter. По крайней мере, не в моей реализации и не в соответствии с моим прочтением стандарта. - person Steve Jessop; 19.03.2010
comment
@Steve: Спасибо, вы и @Potatoswatter, конечно, правы, я неправильно прочитал. Я исправил это до моего понимания. (Вы были правы, думая, что я был в режиме раскрутки стека.) @jdk @Mike, извините за дезинформацию. - person GManNickG; 19.03.2010
comment
throw "this replaces the old exception"; никогда не достигается, потому что функция заканчивается на throw; // and let it go again. - person Oktalist; 15.12.2014