бросить новый std :: exception vs throw std :: exception

113

глядя на код, на который я наткнулся:

throw /*-->*/new std::exception ("//...

и я всегда думал, что тебе здесь не нужно / не стоит использовать new.
Как правильно, оба в порядке, если да, есть ли разница?

Кстати, из того, что я вижу, когда "grepping" с помощью PowerShell boost libs никогда не использовал throw new.

PS также я нашел некоторый код CLI, который использует throw gcnew. Это нормально?

NoSenseEtAl
источник
1
Я думаю, throw gcnewбыло бы полезно, например. если вы хотите, чтобы управляемый код перехватил ваше исключение. Может кто-нибудь поправить меня в этом?
jpalecek 08
1
.Net обрабатывает исключения по указателю, поэтому throw gcnew - правильное решение.
Себастьян Редл
1
@SebastianRedl .Net "указатель" может быть неоднозначным? Хотя gcnew, конечно, нет. System::Exceptionобычно является ссылкой на управляемый объект в куче со сборкой мусора. Я всегда бросал gcnewи ловил System::Exception ^. Конечно, я также постоянно использую finallyC ++ / CLI, хотя не часто смешиваю с исключениями C ++ в одном tryблоке, я не уверен, почему.

Ответы:

89

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

Создание указателя на динамически выделяемый объект - не лучшая идея. Предполагается, что исключения позволят вам написать более надежный код перед лицом ошибок. Если вы выбрасываете объект исключения обычным способом, вы можете быть уверены, что независимо от того, будет ли он пойман предложением catch с указанием правильного типа с помощью a catch (...), будет ли он затем повторно брошен или нет, он будет правильно уничтожен в соответствующее время. (Единственное исключение - ситуация, когда она вообще не обнаруживается, но это неустранимая ситуация, как бы вы на нее ни смотрели.)

Если вы бросаете указатель на динамически выделяемый объект, вы должны быть уверены, что независимо от того, как выглядит стек вызовов в момент, когда вы хотите выбросить свое исключение, есть блок catch, который называет правильный тип указателя и имеет соответствующий deleteвызов. Ваше исключение никогда не должно быть перехвачено, catch (...)если этот блок повторно не генерирует исключение, которое затем перехватывается другим блоком catch, который правильно обрабатывает исключение.

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

CB Bailey
источник
1
стек "выбросить объект исключения" или кучу, друг мой? Стек или куча? (Может быть, я где-то смотрел плохой глобальный пример) о, а если стек, то какова подходящая область видимости?
@ebyrob: Я не совсем уверен, о чем вы спрашиваете, но похоже, что вы хотите знать о хранилище и / или времени жизни объекта исключения, на который можно ответить здесь . Если нет, лучше задать отдельный вопрос.
CB Bailey
31

Нет необходимости использовать newпри выдаче исключения.

Просто пиши:

throw yourexception(yourmessage);

и поймать как:

catch(yourexception const & e)
{
      //your code (probably logging related code)
}

Обратите внимание, что это yourexceptionдолжно происходить std::exceptionпрямо или косвенно.

Наваз
источник
7
Зачем? почему бы не использовать new? почему происходят yourexceptionиз std::exception?
Уолтер
Когда я ленив (а это часто бывает), почему не throw std::exception;работает? g ++, похоже, не скомпилирует его ...
7
@ebyrob: std::exceptionэто тип, и вы не можете выбросить тип , вы должны выбросить объект . Итак, синтаксис должен быть таким: он throw std::exception();будет компилироваться. Насколько это хорошо - это совсем другой вопрос.
Nawaz
22

Бросок new std::exceptionявляется правильным, если сайт вызова ожидает поймать std::exception*. Но никто не рассчитывает поймать указатель на исключение. Даже если вы документируете, что делает ваша функция, и люди читают документацию, они все равно могут забыть и std::exceptionвместо этого попытаться поймать ссылку на объект.


источник
27
Бросок new std::exceptionявляется правильным только в том случае, если сайт вызова ожидает поймать указатель И ожидает взять на себя управление исключением выделения И никогда не будет случаев, когда ваша функция будет вызываться чем-то, что явно не улавливает правильный указатель ( catch(...)или без обработки), иначе произойдет утечка объекта. Короче говоря, это можно приблизительно выразить как «никогда».
CB Bailey
Любопытно, как этот ответ был принят, когда на самом деле это комментарий @ CharlesBailey, который является правильным ответом.
Джон Диблинг
@John: Это тоже пришло мне в голову. Но я думаю, что «один-два удара» имеет хороший эффект, когда я даю сухое резюме, а Чарльз забавно расширяет различные способы, которыми люди могут забыть, как с ними обращаться. Жаль, что вы не получаете репутацию из одобренных комментариев.
Чарльз не дал своего ответа, и у этого A (в отличие от другого) есть объяснения как в A, так и в комментарии.
NoSenseEtAl 08
9

В C ++ FAQ есть хорошее обсуждение этого:

  1. https://isocpp.org/wiki/faq/exceptions#what-to-catch
  2. https://isocpp.org/wiki/faq/exceptions#catch-by-ptr-in-mfc

В основном, «если нет веской причины не ловить по ссылке. Избегайте перехвата по значению, поскольку это приводит к созданию копии, и копия может иметь другое поведение, чем то, что было выброшено. Только в очень особых обстоятельствах вы можете ловить по указателю. "

user1202136
источник
2
Как обычно, в FAQ написано плохо. Вы можете поймать по значению или по ссылке. Указатель оказывается значением (которое вы ловите по значению или ссылке). Помните, что тип Aотличается от типа, A*поэтому, если я это сделаю, throw A()я НЕ смогу поймать catch(A* e)его, потому что это совершенно другой тип.
Мартин Йорк
Эти ссылки сейчас не работают.
stephenspann
1
Исправил ссылки @spanndemic
user1202136
1

Оператор new не может гарантировать, что он никогда не вызовет исключения. По этой причине использование его для выдачи «действительного» (предполагаемого) исключения приведет к созданию кода, который не может гарантировать сбой. Поскольку единовременно может быть только одно исключение, и ваша программа пытается сгенерировать два, прежде чем какое-либо из них может быть обнаружено, лучшее, что может сделать реализация, - это немедленно прервать вашу программу, например, вызвав std :: terminate.

Zkoza
источник