кинути новий std :: виняток проти кинути std :: виключення


113

дивлячись на якийсь код, на який я натрапив:

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

і я завжди думав, що вам це не потрібно / вам не слід newтут користуватися.
Який правильний спосіб, чи обидва в порядку, якщо так, чи є різниця?

До речі, з того, що я бачу під час "схоплення" за допомогою PowerShell boost libs, ніколи не використовую throw new.

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


1
Я думаю, throw gcnewбуло б корисно, наприклад. якщо ви хочете, щоб керований код зафіксував ваше виняток. Хтось може мене виправити на цьому?
jpalecek

1
.Net займається винятками за покажчиком, тому кидати gcnew - це правильно робити.
Себастьян Редл

1
@SebastianRedl .Net "покажчик" може бути неоднозначним? Хоча gcnew точно не є. System::Exceptionяк правило, це посилання на керований об’єкт на купі зібраного сміття. Я завжди кидався gcnewі спіймав System::Exception ^. Звичайно, я finallyвесь час також використовую і C ++ / CLI, хоча не часто змішуюся з винятками C ++ в одному tryблоці, я не знаю чому.

Відповіді:


89

Умовний спосіб кидати та виловлювати винятки - це викинути об’єкт виключення та зловити його посиланням (як правило, constпосиланням). Мова C ++ вимагає від компілятора генерувати відповідний код для побудови об'єкта виключення та належного його очищення у відповідний час.

Кидати вказівник на динамічно виділений об’єкт ніколи не є хорошою ідеєю. Винятки повинні дозволяти вам писати більш надійний код за умови помилок. Якщо ви кидаєте об'єкт винятку звичайним чином, ви можете бути впевнені, що, потрапив він у застереження про вилов, що називає правильний тип, a catch (...), чи він буде потім повторно кинутий чи ні, він буде зруйнований правильно у відповідний час. (Єдиним винятком є ​​те, що його взагалі ніколи не впіймають, але це ситуація, яка не підлягає відновленню, залежно від того, на який ви спосіб ви її дивитесь.)

Якщо ви кидаєте вказівник на динамічно виділений об'єкт, ви повинні бути впевнені, що незалежно від того, як виглядає стек виклику в точці, яку ви хочете викинути, є блок лову, який називає правильний тип вказівника і має відповідний deleteвиклик. Виняток ніколи не повинен бути перехоплений, catch (...)якщо цей блок не перекидає виняток, який потім потрапляє на інший блок лову, який правильно поводиться з виключенням.

Ефективно це означає, що ви скористалися функцією обробки винятків, яка повинна полегшити написання надійного коду і дуже важко писати правильний код у всіх ситуаціях. Це залишає осторонь питання про те, що діяти як код бібліотеки для клієнтського коду, який не очікує на цю функцію, буде майже неможливо.


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();Це складеться. Наскільки це добре, це зовсім інше питання.
Наваз

22

Кидання new std::exceptionправильне, якщо сайт дзвінка очікує зловити std::exception*. Але ніхто не буде сподіватися на те, щоб зловити покажчик на виняток. Навіть якщо ви документуєте саме це і виконує ваша функція, і люди читають документацію, вони все одно можуть забути і спробувати зафіксувати посилання на std::exceptionоб'єкт.


27
Кидання new std::exceptionє правильним лише в тому випадку, якщо сайт виклику очікує зловити вказівник І очікує взяти на себе управління виділенням винятку І ніколи не буде випадків, коли функцію буде викликано чимось, що не чітко вловлює правильний покажчик ( catch(...)або взагалі відсутність обробки), інакше буде витік об'єкта. Коротше кажучи, це можна приблизно оцінити як "ніколи".
CB Bailey

Цікаво , як ця відповідь була прийнятий, коли на самому ділі це @ CharlesBailey в коментарі , що це правильна відповідь.
Джон Даблінг

@John: Це теж перехрестило мою думку. Але я думаю, що один-два удари добре впливає на мене, даючи сухе резюме, а Чарльз забавно розширює різні способи, коли люди зобов’язані забути правильно з цим поводитися. Шкода, що ви не отримаєте репутації на основі голосуючих коментарів.

Чарльз не дав своєї відповіді, і цей А (на відміну від іншого) має пояснення і в А, і в коментарі.
NoSenseEtAl

9

Поширені запитання на C ++ про це добре обговорюють:

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

В основному ", якщо немає поважних причин цього не робити, виловлюйте за посиланням. Уникайте лову за значенням, оскільки це призводить до того, що копія може бути зроблена, і копія може мати іншу поведінку від викинутої. Тільки за дуже особливих обставин ви повинні зловити покажчик. "


2
Як завжди, часто задається FAQ. Ви можете зловити за значенням або довідкою. Вказівник просто буває значенням (яке ви ловите за значенням чи посиланням). Пам'ятайте, що тип Aвідрізняється від типу, A*тому, якщо я це зробити, throw A()я НЕ можу наздогнати, catch(A* e)оскільки це зовсім інший тип.
Мартін Йорк

Ці посилання зараз розірвані.
stephenspann

1
Я виправив посилання @spanndemic
user1202136

1

Оператор new не може гарантувати, що він ніколи не стане винятком. З цієї причини використання його для викидання "дійсного" (призначеного) винятку призведе до отримання коду, який не може бути гарантовано не збійним. Оскільки одночасно може бути лише один виняток, і ваша програма намагається кинути два, перш ніж будь-який з них може бути спійманий, найкраще, що може зробити реалізація, - це негайно перервати програму, наприклад, зателефонувавши на std :: terminate.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.