Влияет ли время существования исключения на другие исключения? - PullRequest
2 голосов
/ 22 декабря 2011

В качестве продолжения моего предыдущего вопроса :

Если я изменю код следующим образом:

struct ExceptionBase : virtual std::exception{};
struct SomeSpecificError : virtual ExceptionBase{};
struct SomeOtherError : virtual ExceptionBase{};

void MightThrow();
void HandleException();
void ReportError();

int main()
{
  try
  {
    MightThrow();
  }
  catch( ... )
  {
    HandleException();
  }
}

void MightThrow()
{
  throw SomeSpecificError();
}

void HandleException()
{
  try
  {
    throw;
  }
  catch( ExceptionBase const & )
  {
    // common error processing
  }

  try
  {
    throw;
  }
  catch( SomeSpecificError const & )
  {
    // specific error processing
  }
  catch( SomeOtherError const & )
  {
    // other error processing
  }

  try
  {
    ReportError();
  }
  catch( ... )
  {
  }
}

void ReportError()
{
  throw SomeOtherError();
}

«Последний обработчик» для оригиналаисключение (т. е. в main) не завершилось, когда выдается второе исключение, поэтому оба исключения активны?Доступно ли исходное исключение после того, как мы оставим обработчик для второго исключения?

Ответы [ 3 ]

2 голосов
/ 22 декабря 2011

C ++ 11 (N3242):

15.1p4: Память для объекта исключения выделяется неопределенным образом, за исключением случаев, отмеченных в 3.7.4.1.Если обработчик завершается повторным сбросом, управление передается другому обработчику для того же исключения.Объект исключения уничтожается после того, как последний оставшийся активный обработчик для исключения исключается любым способом, кроме повторного выброса, или уничтожается последний объект типа std::exception_ptr (18.8.5), который ссылается на объект исключения, в зависимости от того, что наступит позднее.

(std::exception_ptr является функцией C ++ 11 и не используется в вашем примере кода.)

15.3p7: обработчик считается активным, когдаинициализация завершена для формального параметра (если есть) предложения catch.... Обработчик больше не считается активным при выходе из предложения catch или при выходе из режима std::unexpected() после ввода из-за броска.

15.3p8: Исключение - последний активированный обработчик, который все еще активенназывается обработанным в данный момент исключением .

15.1p8: throw-выражение без операнда сбрасывает текущее обработанное исключение (15.3).

Или эквивалентно, я думаю, throw; всегда относится к исключению, перехваченному самым внутренним блоком перехвата, который выполняется в данный момент.За исключением того, что я не определил «самый внутренний» и «выполняющий» так же тщательно, как Стандарт определил все свои термины выше.

И да, одновременно может быть выделено более одного объекта исключения, и требуется C ++чтобы они жили достаточно долго, чтобы делать «правильные вещи», когда вы пытаетесь свергнуть.

1 голос
/ 22 декабря 2011

Не уверен, насколько это стандартно, но если я добавлю throw; прямо перед концом HandleException и скомпилирую с g ++, результирующая программа скажет мне следующее:

root@xxxx [~/code]# ./a.out
terminate called after throwing an instance of 'SomeSpecificError'
  what():  17SomeSpecificError
Aborted

Обратите внимание на исключениетип.Это исключение, которое было добавлено в MightThrow, а не из ReportError.

VS2010 также сообщает SomeSpecificError.

0 голосов
/ 22 декабря 2011

Существует только одно «активное исключение» одновременно. Когда вы throw делаете еще одно исключение в обработчике исключений, фактически вы меняете тип исключения, которое распространяется по стеку.

(Кстати, все ли это необходимо? Вы действительно находите этот код легко читаемым?)

[обновление]

Что касается стандартной ссылки ... ИСО / МЭК 14882: 2003, раздел 15.3 [кроме ручки], пункт 8 гласит:

Исключение считается обработанным при входе в обработчик. [Обратите внимание стек будет размотан в этот момент. ]

Итак, еще один способ сказать, что как только вы входите в блок catch, исходное исключение больше не активно.

Кроме того, функция uncaught_exception() вернет false, как только будет введен блок catch. Раздел 15.5.3 [исключая .uncaught] гласит:

Функция

bool uncaught_exception() throw()

возвращает true после завершения оценки объекта, который должен быть брошен, до завершения инициализация объявления исключения в соответствующем обработчике (18.6.4). Это включает в себя разматывание стека. Если исключение переброшено (15.1), uncaught_exception () возвращает истину с точки повторного броска пока вновь возникшее исключение не будет поймано снова.

[обновление 2]

Также актуален пункт 15.3 параграфа 4:

Память для временной копии создаваемого исключения: распределяется неопределенным образом, за исключением случаев, указанных в 3.7.3.1. временно сохраняется до тех пор, пока выполняется обработчик для это исключение. В частности, если обработчик завершает работу, выполняя throw; оператор, который передает управление другому обработчику для того же исключение, поэтому временное остается. Когда последний обработчик выполнено для исключений выходит любым способом, кроме throw; временный объект уничтожен и реализация может быть освобождена память для временного объекта; любое такое освобождение делается в неуказанный способ. Разрушение происходит сразу после уничтожение объекта, заявленного в декларации-исключении в обработчик.

Таким образом, исходное исключение уничтожается, как только завершается работа с обработчиком любым другим способом, кроме «голого» * ​​1041 *. Так что если вы throw какое-то другое исключение, оно выходит из обработчика и уничтожает исходное исключение.

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