Вы (действительно) пишете безопасный код исключения? [закрыто]


312

Обработка исключений (EH), кажется, является текущим стандартом, и, ища в Интернете, я не могу найти какие-либо новые идеи или методы, которые пытаются улучшить или заменить его (ну, некоторые вариации существуют, но ничего нового).

Хотя большинство людей, кажется, игнорируют это или просто принимают, EH имеет некоторые огромные недостатки: исключения невидимы для кода, и это создает много-много возможных точек выхода. Джоэл о программном обеспечении написал статью об этом . Сравнение с gotoидеальной идеей заставило меня снова задуматься об EH.

Я стараюсь избегать EH и просто использовать возвращаемые значения, обратные вызовы или что-то еще, соответствующее цели Но когда вам нужно написать надежный код, вы просто не можете игнорировать EH в наши дни : он начинается с new, который может вызвать исключение, а не просто возвращать 0 (как в старые времена). Это делает любую строку кода C ++ уязвимой для исключения. А затем еще больше мест в основополагающем коде C ++ генерируют исключения ... std lib делает это и так далее.

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

Но это тяжело, это действительно тяжело. Вы должны научиться писать безопасный код исключений, и даже если у вас есть некоторый опыт работы с ним, все равно потребуется перепроверить любую отдельную строку кода, чтобы быть безопасным! Или вы начинаете помещать блоки try / catch везде, что загромождает код, пока он не достигнет состояния нечитаемости.

EH заменил старый чистый детерминистический подход (возвращаемые значения ..), который имел всего несколько, но понятных и легко решаемых недостатков, подходом, который создает множество возможных точек выхода в вашем коде, и если вы начинаете писать код, который перехватывает исключения (то, что вы вынуждены делать это в какой-то момент), затем он даже создает множество путей через ваш код (код в блоках перехвата, подумайте о серверной программе, где вам нужны средства ведения журналов, отличные от std :: cerr ..). У EH есть преимущества, но это не главное.

Мои актуальные вопросы:

  • Вы действительно пишете код безопасности исключений?
  • Вы уверены, что ваш последний «готовый к работе» код безопасен для исключений?
  • Можете ли вы быть уверены, что это так?
  • Вы знаете и / или действительно используете альтернативы, которые работают?

44
«EH заменил старый чистый детерминистический подход (возвращаемые значения ..)» Что? Исключения так же детерминированы, как и коды возврата. В них нет ничего случайного.
rlbond

2
EH долгое время был стандартом (даже с Ады!)

12
Является ли "EH" установленным сокращением для обработки исключений? Я никогда не видел это раньше.
Томас Падрон-Маккарти

3
Законы Ньютона применимы сегодня для большинства вещей. Тот факт, что они предсказывали очень широкий спектр явлений на протяжении веков, имеет большое значение.
Дэвид Торнли

4
@frunsi: Хех, прости, что сбил тебя с толку! Но я думаю, что, во всяком случае, странная и необъяснимая аббревиатура или аббревиатура будут обескураживать людей больше, чем нет.
Томас Падрон-Маккарти

Ответы:


520

В вашем вопросе утверждается, что «написание исключительного кода очень сложно». Сначала я отвечу на ваши вопросы, а затем отвечу на скрытый за ними вопрос.

Отвечая на вопросы

Вы действительно пишете код безопасности исключений?

Конечно, я делаю.

По этой причине Java потеряла свою привлекательность для меня как программиста C ++ (отсутствие семантики RAII), но я отвлекаюсь: это вопрос C ++.

Фактически это необходимо, когда вам нужно работать с кодом STL или Boost. Например, потоки C ++ ( boost::threadили std::thread) сгенерируют исключение для корректного выхода.

Вы уверены, что ваш последний «готовый к работе» код безопасен для исключений?

Можете ли вы быть уверены, что это так?

Написание кода, исключающего исключения, похоже на написание кода без ошибок.

Вы не можете быть на 100% уверены, что ваш код безопасен от исключений. Но затем вы стремитесь к этому, используя известные шаблоны и избегая известных анти-шаблонов.

Вы знаете и / или действительно используете альтернативы, которые работают?

В C ++ нет жизнеспособных альтернатив (т.е. вам нужно вернуться к C и избежать библиотек C ++, а также внешних неожиданностей, таких как Windows SEH).

Написание исключительного безопасного кода

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

Например, a newможет выдать исключение, но назначение встроенного (например, int или указателя) не завершится неудачей. Своп никогда не потерпит неудачу (никогда не пишите своп броска), std::list::push_backможет бросить ...

Гарантия исключения

Первое, что нужно понять, это то, что вы должны быть в состоянии оценить гарантию исключения, предлагаемую всеми вашими функциями:

  1. none : ваш код никогда не должен предлагать это. Этот код пропустит все и сломается при первом же исключении.
  2. базовая : это гарантия, которую вы должны предоставить по меньшей мере, то есть, если выдается исключение, ресурсы не просачиваются, а все объекты остаются целыми
  3. сильный : обработка либо завершится успешно, либо сгенерирует исключение, но если он сгенерирует, то данные будут в том же состоянии, как если бы обработка вообще не началась (это дает транзакционную мощность C ++)
  4. nothrow / nofail : обработка будет успешной.

Пример кода

Следующий код кажется правильным C ++, но на самом деле предлагает гарантию «none», и, следовательно, он не корректен:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   X * x = new X() ;                // 2. basic : can throw with new and X constructor
   t.list.push_back(x) ;            // 3. strong : can throw
   x->doSomethingThatCanThrow() ;   // 4. basic : can throw
}

Я пишу весь свой код с учетом такого анализа.

Самая низкая предлагаемая гарантия - базовая, но при этом порядок каждой инструкции делает всю функцию «нулевой», потому что, если 3. throws, x утечет.

Первое, что нужно сделать, это сделать функцию «базовой», то есть поместить умный указатель в x, пока он не будет безопасно принадлежать списку:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   std::auto_ptr<X> x(new X()) ;    // 2.  basic : can throw with new and X constructor
   X * px = x.get() ;               // 2'. nothrow/nofail
   t.list.push_back(px) ;           // 3.  strong : can throw
   x.release() ;                    // 3'. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 4.  basic : can throw
}

Теперь наш код предлагает «базовую» гарантию. Ничего не протечет, и все объекты будут в правильном состоянии. Но мы могли бы предложить больше, то есть сильную гарантию. Вот где это может стать дорогостоящим, и поэтому не весь код C ++ является сильным. Давай попробуем:

void doSomething(T & t)
{
   // we create "x"
   std::auto_ptr<X> x(new X()) ;    // 1. basic : can throw with new and X constructor
   X * px = x.get() ;               // 2. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 3. basic : can throw

   // we copy the original container to avoid changing it
   T t2(t) ;                        // 4. strong : can throw with T copy-constructor

   // we put "x" in the copied container
   t2.list.push_back(px) ;          // 5. strong : can throw
   x.release() ;                    // 6. nothrow/nofail
   if(std::numeric_limits<int>::max() > t2.integer)  // 7.   nothrow/nofail
      t2.integer += 1 ;                              // 7'.  nothrow/nofail

   // we swap both containers
   t.swap(t2) ;                     // 8. nothrow/nofail
}

Мы переупорядочили операции, сначала создав и установив Xих правильное значение. Если какая-либо операция завершается неудачно, то tона не изменяется, поэтому операции с 1 по 3 можно считать «сильной»: если что-то выбрасывается, tне изменяется и Xне будет течь, потому что принадлежит интеллектуальному указателю.

Затем мы создаем копию t2из t, и работать на этой копии от операции 4 до 7. Если что - то бросает, t2модифицируется, но затем, по- tпрежнему является оригинальным. Мы все еще предлагаем сильную гарантию.

Затем мы переставляем tи t2. Операции подкачки должны быть nothrow в C ++, поэтому будем надеяться, что Tподкачка, для которой вы написали, - nothrow (если это не так, переписайте ее, чтобы она не стала выше).

Таким образом, если мы достигнем конца функции, все выполнится успешно (нет необходимости в возвращаемом типе) и будет tиметь свое исключенное значение. Если это не удастся, значит t, все еще имеет свое первоначальное значение.

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

Вывод

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

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

Нормальная ошибка против ошибки

Как программист может гарантировать, что безотказная функция всегда будет успешной? В конце концов, функция может иметь ошибку.

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

Исключения - исключительная ошибка обработки, а не ошибки кода.

Последние слова

Теперь вопрос: «Стоит ли это того?».

Конечно, это является. Наличие функции «nothrow / no-fail», зная, что функция не потерпит неудачу, является большим благом. То же самое можно сказать и о «сильной» функции, которая позволяет писать код с семантикой транзакций, такой как базы данных, с функциями фиксации / отката, когда коммит является нормальным выполнением кода, а исключения - откатом.

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

Итак, насколько я понимаю, это является стоит.

Редактировать 2010-01-29: О безбросовом свопе

nobar сделал комментарий, который, на мой взгляд, весьма актуален, поскольку он является частью «как вы пишете безопасный код исключения»:

  • [меня] своп никогда не подведет (даже не пишите своп)
  • [nobar] Это хорошая рекомендация для пользовательских swap()функций. Следует отметить, однако, что std::swap()может произойти сбой на основе операций, которые он использует внутри

по умолчанию std::swapбудут создаваться копии и назначения, которые для некоторых объектов могут генерировать. Таким образом, подкачка по умолчанию может сгенерировать, либо для ваших классов, либо даже для классов STL. Что касается стандарта C ++, то операция swap для vector, dequeи listне будет выбрасываться, тогда как это возможно, mapесли функтор сравнения может сгенерировать конструкцию копирования (см. Язык программирования C ++, Специальный выпуск, приложение E, E.4.3 Своп )

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

Таким образом, исходный текст остается в силе: никогда не пишите своп-бросок, но необходимо запомнить комментарий Нобара: убедитесь, что объекты, которые вы меняете, имеют не-бросающий своп.

Редактировать 2011-11-06: Интересная статья

Дейв Абрахамс , который дал нам основные / сильные / nothrow гарантии , в своей статье описал свой опыт по обеспечению безопасности исключения STL:

http://www.boost.org/community/exception_safety.html

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

Изменить 2013-05-31: Комментарий от дионадар

t.integer += 1;без гарантии, что переполнение не произойдет, не исключение безопасно, и на самом деле может технически вызвать UB! (Переполнением со знаком является UB: C ++ 11 5/4 «Если во время вычисления выражения результат не определен математически или не находится в диапазоне представляемых значений для его типа, поведение не определено.») Обратите внимание, что без знака целые числа не переполняются, но выполняют вычисления в классе эквивалентности по модулю 2 ^ # бит.

Dionadar ссылается на следующую строку, которая действительно имеет неопределенное поведение.

   t.integer += 1 ;                 // 1. nothrow/nofail

Решение здесь состоит в том, чтобы проверить, является ли целое число уже в его максимальном значении (использование std::numeric_limits<T>::max()) перед выполнением сложения.

Моя ошибка будет в разделе «Нормальная ошибка против ошибки», то есть ошибка. Это не лишает законной силы аргументацию и не означает, что безопасный код исключений бесполезен, потому что его невозможно достичь. Вы не можете защитить себя от выключения компьютера, ошибок компиляции, даже ваших ошибок или других ошибок. Вы не можете достичь совершенства, но вы можете попытаться приблизиться как можно ближе.

Я исправил код с учетом комментария Дионадара.


6
Большое спасибо! Я все еще надеялся на что-то другое. Но я принимаю ваш ответ за то, что вы придерживаетесь C ++ и за ваше подробное объяснение. Вы даже опровергли мое нарушение «Это делает любую строку кода C ++ уязвимой для исключения». Этот построчный анализ имеет смысл в конце концов ... Я должен подумать об этом.
Фрунси

8
«Своп никогда не подведет». Это хорошая рекомендация для пользовательских функций swap (). Однако следует отметить, что std :: swap () может завершиться с ошибкой в ​​зависимости от операций, которые он использует внутри.
Нобар

7
@ Mehrdad: "finally" does exactly what you were trying to achieveКонечно, это так. И поскольку с помощью него легко создавать хрупкий код finally, в Java 7 было наконец введено понятие «попробуй с ресурсом» (то есть через 10 лет после C # usingи 3 десятилетия после деструкторов C ++). Именно эту хрупкость я критикую. Что касается Just because [finally] doesn't match your taste (RAII doesn't match mine, [...]) doesn't mean it's "failing": в этом индустрия не согласна с вашим вкусом, так как языки сборки мусора, как правило, добавляют операторы, основанные на RAII (C # usingи Java try).
paercebal

5
@ Mehrdad: RAII doesn't match mine, since it needs a new struct every single darn time, which is tedious sometimesНет, это не так. Вы можете использовать умные указатели или служебные классы для «защиты» ресурсов.
paercebal

5
@Mehrdad: «это заставляет вас создавать оболочку для чего-то, для чего вам может не понадобиться оболочка» - при кодировании в стиле RAII вам не понадобятся никакие виды оболочек: ресурс - это объект, а время жизни объекта - время жизни ресурса. Однако я, выходец из мира C ++, в настоящее время борюсь с Java-проектом. По сравнению с RAII в C ++ это «ручное управление ресурсами» в Java - МЕСЬКО! Мое мнение, но Java давно поменялась на «автоматическая память mgmt» на «автоматический ресурс mgmt».
Фрунси

32

Написание безопасного для исключений кода на C ++ - это не столько использование множества блоков try {} catch {}. Речь идет о документировании того, какие гарантии предоставляет ваш код.

Я рекомендую прочитать серию « Гуру недели» Херба Саттера , в частности, серии 59, 60 и 61.

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

  • Базовый: когда ваш код выдает исключение, ваш код не пропускает ресурсы, а объекты остаются разрушаемыми.
  • Сильный: когда ваш код генерирует исключение, он оставляет состояние приложения без изменений.
  • Нет броска: ваш код никогда не выбрасывает исключения.

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


1
Его книга «Исключительный C ++» также хорошо читается. Но я все еще пытаюсь поставить под сомнение концепцию EH ...
Фрунси

8
+1 OP объединяет обработку исключений (их отлов) с безопасностью исключений (обычно больше о RAII)
jk.

Я подозреваю, что очень мало производственного кода C ++ безопасно для исключений.
Raedwald

18

Некоторые из нас используют исключения уже более 20 лет. Например, они есть у PL / I. Предпосылка, что они являются новой и опасной технологией, кажется мне сомнительной.


Пожалуйста, не поймите меня неправильно, я (или пытаюсь) допросить EH. И особенно C ++ EH. И я ищу альтернативы. Может быть, я должен принять это (и я буду, если это единственный путь), но я думаю, что могут быть лучшие альтернативы. Не то, чтобы я думал, что концепция является новой, но да, я думаю, что это может быть более опасным, чем явная обработка ошибок с кодами возврата ...
Фрунси,

1
Если вам это не нравится, не используйте его. Поместите блоки try вокруг вещей, которые вы должны вызывать, которые могут вызвать, и заново изобрели ye-olde-error-code, и пострадайте от проблем, которые у него есть, которые в некоторых случаях допустимы.
bmargulies

Хорошо, отлично, я просто буду использовать EH и коды ошибок и жить с этим. Я ненормальный, я должен был прийти к этому решению самостоятельно! ;)
Фрунси

17

Прежде всего (как сказал Нил), SEH - это структурированная обработка исключений Microsoft. Это похоже, но не идентично обработке исключений в C ++. Фактически, вы должны включить обработку исключений C ++, если вы хотите это в Visual Studio - поведение по умолчанию не гарантирует, что локальные объекты будут уничтожены во всех случаях! В любом случае, обработка исключений на самом деле не сложнее, она просто другая .

Теперь для ваших актуальных вопросов.

Вы действительно пишете код безопасности исключений?

Да. Я стремлюсь к исключению безопасного кода во всех случаях. Я проповедую, используя методы RAII для ограниченного доступа к ресурсам (например, boost::shared_ptrдля памяти, boost::lock_guardдля блокировки). В целом, последовательное использование RAII и методов защиты контекста значительно облегчит написание кода, безопасного для исключений. Хитрость заключается в том, чтобы узнать, что существует и как его применять.

Вы уверены, что ваш последний «готовый к работе» код безопасен для исключений?

Нет. Это так же безопасно, как и. Могу сказать, что я не видел сбоя процесса из-за исключения в течение нескольких лет круглосуточной работы. Я не ожидаю идеального кода, просто хорошо написанный код. В дополнение к обеспечению безопасности исключений, описанные выше методы гарантируют правильность способом, которого практически невозможно достичь с помощью try/ catchблоков. Если вы перехватываете все в своей верхней области управления (поток, процесс и т. Д.), То вы можете быть уверены, что продолжите работать в условиях исключений ( большую часть времени ). Те же самые приемы также помогут вам продолжать работать правильно, несмотря на исключения без блоков try/ catchвезде .

Можете ли вы быть уверены, что это так?

Да. Вы можете быть уверены в тщательном аудите кода, но никто этого не делает? Регулярные обзоры кода и осторожные разработчики имеют большое значение для достижения этой цели.

Вы знаете и / или действительно используете альтернативы, которые работают?

За несколько лет я попробовал несколько вариантов, таких как состояния кодирования в старших битах (ala HRESULTs ) или этот ужасный setjmp() ... longjmp()хак. Оба из них ломаются на практике, хотя совершенно по-разному.


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

  • Вы только хотите увидеть try/catch когда вы можете сделать что-то с конкретным исключением
  • Вы почти никогда не хотите видеть сырой newилиdelete в коде
  • Eschew std::sprintf, snprintfи массивы в целом - используйте std::ostringstreamдля форматирования и замены массивов на std::vectorиstd::string
  • Если вы сомневаетесь, поищите функциональность в Boost или STL, прежде чем запускать свой собственный

Я могу только порекомендовать вам научиться правильно использовать исключения и забыть о кодах результата, если вы планируете писать на C ++. Если вы хотите избежать исключений, вы можете рассмотреть возможность написания на другом языке, который либо не имеет их, либо делает их безопасными . Если вы действительно хотите научиться полностью использовать C ++, прочитайте несколько книг Херба Саттера , Николая Йосуттиса и Скотта Мейерса .


«Поведение по умолчанию не гарантирует, что локальные объекты уничтожаются во всех случаях». Вы говорите, что настройка по умолчанию компилятора Visual Studio C ++ создает код, который является некорректным перед лицом исключений. Это действительно так?
Raedwald

«Вы почти никогда не хотите видеть необработанный newили deleteв коде»: под необработанным, я полагаю, вы имеете в виду вне конструктора или деструктора.
Raedwald

@Raedwald - re: VC ++: версия VC ++ VS2005 не будет уничтожать локальные объекты при возникновении исключения SEH. Прочтите «Включение обработки исключений в C ++» . В VS2005 исключения SEH по умолчанию не вызывают деструкторы объектов C ++. Если вы вызываете функции Win32 или что-либо определенное в DLL-интерфейсе C-интерфейса, вам придется беспокоиться об этом, поскольку они могут (и будут иногда) генерировать исключение SEH по-вашему.
Д.Шоули

@Raedwald: re: raw : в принципе, deleteникогда не должен использоваться вне реализации tr1::shared_ptrи тому подобное. newможет быть использован при условии, что его использование что-то вроде tr1::shared_ptr<X> ptr(new X(arg, arg));. Важной частью является то, что результат newнепосредственно переходит в управляемый указатель. Страница на boost::shared_ptrBest Practices описывает лучшее.
Д.Шоули

Почему вы ссылаетесь на std :: sprintf (et al) в вашем наборе правил для обеспечения безопасности исключений? Они не документируют, что они генерируют
mabraham

10

Невозможно написать безопасный для исключения код в предположении, что «любая строка может выдать». Разработка кода, исключающего исключительные ситуации, основывается на определенных контрактах / гарантиях, которые вы должны ожидать, соблюдать, соблюдать и реализовывать в своем коде. Это абсолютно необходимо иметь код, который гарантированно никогда не выкинет. Существуют и другие виды исключительных гарантий.

Другими словами, создание кода, исключающего исключения, в значительной степени является вопросом проектирования программы, а не просто вопросом простого кодирования .


8
  • Вы действительно пишете код безопасности исключений?

Ну, я определенно собираюсь.

  • Вы уверены, что ваш последний «готовый к работе» код безопасен для исключений?

Я уверен, что мои 24/7 серверы, созданные с использованием исключений, работают круглосуточно и не пропускают память.

  • Можете ли вы быть уверены, что это так?

Очень трудно быть уверенным, что любой код верен. Как правило, можно идти только по результатам

  • Вы знаете и / или действительно используете альтернативы, которые работают?

Нет. Использование исключений проще и проще, чем любая из альтернатив, которые я использовал в программировании за последние 30 лет.


30
Этот ответ не имеет значения.
Мэтт Джоунер

6
@MattJoiner Тогда вопрос не имеет значения.
Майлз Рут

5

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

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


3

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


2
Это намного лучше при разумном использовании noexcept.
einpoklum

2

Мне действительно нравится работать с Eclipse и Java (хотя и новичок в Java), потому что он выдает ошибки в редакторе, если вам не хватает обработчика EH. Это делает вещи намного сложнее забыть, чтобы обработать исключение ...

Кроме того, с помощью инструментов IDE он автоматически добавляет блок try / catch или другой блок catch.


5
В этом разница между проверенными (Java) и непроверенными (c ++) исключениями (в Java их тоже есть несколько). Преимущество проверенных исключений заключается в том, что вы написали, но там у него есть свои недостатки. Гугл за разницу подходов и разных проблем у них.
Дэвид Родригес - dribeas

2

Некоторые из нас предпочитают такие языки, как Java, которые заставляют нас объявлять все исключения, создаваемые методами, вместо того, чтобы делать их невидимыми, как в C ++ и C #.

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

При этом низкоуровневое программирование API-библиотеки, вероятно, должно избегать обработки исключений и придерживаться кодов возврата ошибок.

По моему опыту, трудно написать чистый код обработки исключений на C ++. Я использую new(nothrow)много.


4
И вы также избегаете большей части стандартной библиотеки? Использование new(std::nothrow)не достаточно.
Между прочим,

3
Удобство использования проверенных исключений в Java сильно преувеличено. На самом деле не-Java языки, они НЕ считаются успешными. Вот почему выражение «throw» в C ++ теперь считается устаревшим, и C # никогда серьезно не задумывался об их реализации (это был проектный выбор). Для Java следующий документ может быть освещен: googletesting.blogspot.com/2009/09/…
paercebal

2
По моему опыту, написание кода, исключающего исключения, на C ++ не так сложно, и в целом это приводит к более чистому коду. Конечно, вы должны научиться делать это.
Дэвид Торнли

2

Я стараюсь изо всех сил, чтобы написать исключительный код, да.

Это означает , что я позаботьтесь , чтобы следить за который строки могут быть выброшены. Не каждый может, и это крайне важно помнить об этом. Главное - подумать и спроектировать свой код так, чтобы он удовлетворял гарантиям исключения, определенным в стандарте.

Можно ли написать эту операцию, чтобы обеспечить гарантию исключений? Должен ли я согласиться на основной? Какие строки могут выдавать исключения, и как я могу гарантировать, что если они это сделают, они не повредят объект?


2
  • Вы действительно пишете код безопасности исключений? [Нет такого понятия. Исключения - это защита от ошибок, если у вас нет управляемой среды. Это относится к первым трем вопросам.]

  • Вы знаете и / или действительно используете альтернативы, которые работают? [Альтернатива чему? Проблема в том, что люди не отделяют реальные ошибки от нормальной работы программы. Если это нормальная работа программы (т.е. файл не найден), это не совсем обработка ошибок. Если это фактическая ошибка, нет способа «обработать» ее, или это не фактическая ошибка. Ваша цель здесь - выяснить, что пошло не так, и либо остановить электронную таблицу и записать ошибку, перезапустить драйвер на тостер, либо просто помолиться о том, чтобы реактивный истребитель продолжал летать, даже если его программное обеспечение глючит, и надеяться на лучшее.]


0

Многие (я бы даже сказал, большинство) делают.

Что действительно важно в отношении исключений, так это то, что если вы не пишете какой-либо код обработки - результат будет совершенно безопасным и хорошо себя ведет. Слишком жаждущий паники, но в безопасности.

Вам нужно активно делать ошибки в обработчиках, чтобы получить что-то небезопасное, и только catch (...) {} будет сравниваться с игнорированием кода ошибки.


4
Не правда. Очень легко написать код, который не безопасен для исключений. Например: f = new foo(); f->doSomething(); delete f;если метод doSomething выдает исключение, у вас есть утечка памяти.
Кристофер Джонсон

1
Не имеет значения, когда ваша программа завершается, верно? Чтобы продолжить выполнение, вам придется активно проглатывать исключения. Ну, есть некоторые конкретные случаи, когда завершение без очистки все еще неприемлемо, но такие ситуации требуют особого внимания в любом языке программирования и стиле.
Има

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

1
Если вы игнорируете исключения в коде приложения, программа может по-прежнему НЕ вести себя хорошо, когда задействованы внешние ресурсы. Правда, ОС заботится о закрытии файловых дескрипторов, блокировок, сокетов и так далее. Но не все обрабатывается, например, он может оставить ненужные файлы или повредить файлы во время записи в них и так далее. Если вы игнорируете исключения, у вас есть проблема в Java, в C ++ вы можете работать с RAII (но когда вы используете технику RAII, вы, скорее всего, используете их, потому что заботитесь об исключениях) ...
Фрунси

3
Пожалуйста, не скручивай мои слова. Я написал «если вы не пишете какой-либо код обработки» - и я имел в виду именно это. Чтобы игнорировать исключения, вам нужно написать код.
Има
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.