Я всегда верил, что если метод может выдать исключение, то неосторожно не защищать этот вызов значимым блоком try.
Я только что написал: « Вы должны ВСЕГДА оборачивать вызовы, которые могут бросать попытки, ловить блоки. на этот вопрос и мне сказали, что это «удивительно плохой совет» - я хотел бы понять, почему.
Ответы:
Метод должен перехватывать исключение только тогда, когда он может обрабатывать его каким-либо разумным способом.
В противном случае передайте его в надежде, что метод, находящийся выше стека вызовов, сможет это понять.
Как уже отмечали другие, хорошей практикой является наличие обработчика необработанных исключений (с ведением журнала) на самом высоком уровне стека вызовов, чтобы гарантировать, что любые фатальные ошибки регистрируются.
источник
try
блоки. Хорошая дискуссия Скотта Мейерса «Более эффективный C ++».try
блоки бесплатны в любом современном C-компиляторе, эта информация датируется Ником. Я также не согласен с наличием обработчика исключений верхнего уровня, потому что вы теряете информацию о местонахождении (фактическое место, где инструкция не выполнена).terminate
) , Это скорее механизм безопасности. Кроме того,try/catch
более или менее бесплатны, когда нет никаких исключений. Когда есть один размножающийся, он потребляет время каждый раз, когда его бросают и ловят, так что цепочкаtry/catch
этого единственного повторного броска не является бесплатной.Как Митч и другие заявили, вы не должны поймать исключение, которое вы не планируете обрабатывать каким-либо образом. Вы должны учитывать, как приложение будет систематически обрабатывать исключения при разработке. Это обычно приводит к тому, что уровни обработки ошибок основаны на абстракциях - например, вы обрабатываете все ошибки, связанные с SQL, в своем коде доступа к данным, так что часть приложения, взаимодействующая с объектами домена, не подвержена тому факту, что это где-то БД под капотом.
Есть несколько связанных запахов кода, которые вы определенно хотите избежать, в дополнение к запаху "лови все везде" .
«catch, log, rethrow» : если вы хотите вести журнал на основе областей , то напишите класс, который выдает оператор log в своем деструкторе, когда стек разворачивается из-за исключения (ala
std::uncaught_exception()
). Все, что вам нужно сделать, это объявить экземпляр журнала в интересующей вас области, и, вуаля, у вас есть журналирование и нет ненужныхtry
/catch
логики.«поймай, брось переведенный» : это обычно указывает на проблему абстракции. Если вы не внедряете федеративное решение, в котором вы переводите несколько конкретных исключений в одно более общее, вы, вероятно, имеете ненужный уровень абстракции ... и не говорите, что "мне это может понадобиться завтра" .
«лови, убирай, отбрасывай» : это одна из моих любимых мозолей. Если вы видите много это, то вам следует обратиться Получение ресурса есть инициализация методы и поместить часть очистки в деструкторе дворника экземпляра объекта.
Я считаю, что код, усеянный символом
try
/catch
blocks, является хорошей целью для проверки и рефакторинга кода. Это указывает на то, что либо обработка исключений не совсем понятна, либо код стал амебой и нуждается в серьезном рефакторинге.источник
Logger
класс, аналогичный тому,log4j.Logger
который включает идентификатор потока в каждой строке журнала, и выдавал предупреждение в деструкторе, когда исключение было активным.Потому что следующий вопрос: «Я поймал исключение, что мне делать дальше?» Что ты бы сделал? Если вы ничего не делаете - это скрытие ошибок, и программа может «просто не работать» без какой-либо возможности выяснить, что произошло. Вы должны понимать, что именно вы будете делать, как только вы поймали исключение, и поймать только, если вы знаете.
источник
Вам не нужно покрывать каждый блок try-catches, потому что try-catch может по-прежнему перехватывать необработанные исключения, которые вызываются в функциях ниже стека вызовов. Таким образом, вместо того, чтобы каждая функция имела функцию try-catch, у вас может быть одна на логике верхнего уровня вашего приложения. Например, может существовать
SaveDocument()
подпрограмма верхнего уровня, которая вызывает много методов, которые вызывают другие методы и т. Д. Эти под-методы не нуждаются в своих собственных попытках перехвата, потому что, если они выдают, он все еще перехватываетсяSaveDocument()
catch.Это хорошо по трем причинам: это удобно, потому что у вас есть единственное место, чтобы сообщить об ошибке:
SaveDocument()
блок (и) catch. Нет необходимости повторять это во всех под-методах, и в любом случае это то, что вы хотите: одно единственное место, чтобы дать пользователю полезную диагностику о том, что пошло не так.Во-вторых, сохранение отменяется всякий раз, когда выдается исключение. С каждым суб-методом проб выделяющийся, если исключение, вы получите , чтобы поймать блок этого метода, листьев выполнения функции, и она осуществляет через
SaveDocument()
. Если что-то уже пошло не так, вы, вероятно, хотите остановиться прямо здесь.Три, все ваши подчиненные методы могут предполагать, что каждый вызов успешен . Если вызов не удался, выполнение перейдет к блоку catch, а последующий код никогда не будет выполнен. Это может сделать ваш код намного чище. Например, вот с кодами ошибок:
Вот как это может быть написано с исключениями:
Теперь стало намного понятнее, что происходит.
Заметьте, что безопасный код исключения может быть сложнее написать другими способами: вы не хотите терять память при возникновении исключения. Убедитесь, что вы знаете о RAII , STL-контейнерах, интеллектуальных указателях и других объектах, которые освобождают свои ресурсы в деструкторах, поскольку объекты всегда уничтожаются перед исключениями.
источник
try
-catch
блоки , которые пытаются флаг до каждой несколько иной перестановки некоторой ошибки с немного другим сообщением, когда в действительности все они должны закончить то же самое: сделки или программы отказа и выхода! Если происходит отказ, достойный исключительной ситуации, я держу пари, что большинство пользователей просто хотят спасти то, что они могут, или, по крайней мере, оставить в покое, не имея дело с 10 уровнями сообщений об этом.Херб Саттер писал об этой проблеме здесь . Наверняка стоит прочесть.
Тизер:
источник
Как указано в других ответах, вы должны поймать исключение, только если вы можете сделать какую-то разумную обработку ошибок для него.
Например, в вопросе, который породил ваш вопрос, спрашивающий спрашивает, безопасно ли игнорировать исключения для целого
lexical_cast
числа в строку. Такой актерский состав никогда не должен подвести. Если это не помогло, в программе что-то пошло не так. Что вы могли бы сделать, чтобы восстановиться в этой ситуации? Вероятно, лучше просто позволить программе умереть, поскольку она находится в состоянии, которому нельзя доверять. Поэтому не обрабатывать исключение может быть самым безопасным.источник
Если вы всегда обрабатываете исключения сразу в вызывающей стороне метода, который может вызвать исключение, тогда исключения становятся бесполезными, и вам лучше использовать коды ошибок.
Весь смысл исключений в том, что они не должны обрабатываться в каждом методе в цепочке вызовов.
источник
Лучший совет, который я услышал, заключается в том, что вы должны ловить исключения только в тех местах, где вы можете разумно что-то делать с исключительным условием, и что «ловить, регистрировать и освобождать» не является хорошей стратегией (если ее иногда можно избежать в библиотеках).
источник
Я согласен с основным направлением вашего вопроса, чтобы обрабатывать как можно больше исключений на самом низком уровне.
Некоторые из существующих ответов звучат так: «Вам не нужно обрабатывать исключение. Кто-то другой сделает это в стеке». По моему опыту, это плохое оправдание, чтобы не думать об обработке исключений в разрабатываемом сейчас фрагменте кода, делая исключение обработкой проблемы кого-то еще или позже.
Эта проблема резко возрастает в распределенной разработке, где вам может потребоваться вызвать метод, реализованный коллегой. А затем вам нужно проверить вложенную цепочку вызовов методов, чтобы выяснить, почему он / она выдает вам какое-то исключение, которое могло бы быть обработано намного проще при самом глубоком вложенном методе.
источник
Мой профессор компьютерных наук однажды дал мне совет: «Используйте блоки Try и Catch только тогда, когда невозможно обработать ошибку стандартными средствами».
В качестве примера он сказал нам, что если у программы возникла серьезная проблема в месте, где невозможно сделать что-то вроде:
Тогда вы должны использовать try, catch блоков. Хотя вы можете использовать исключения для обработки этого, как правило, это не рекомендуется, потому что исключения являются дорогостоящими с точки зрения производительности.
источник
Если вы хотите проверить результат каждой функции, используйте коды возврата.
Цель исключений состоит в том, что вы можете часто проверять результаты МЕНЬШЕ. Идея состоит в том, чтобы отделить исключительные (необычные, более редкие) условия от вашего более обычного кода. Это делает обычный код чище и проще - но все же способен справляться с этими исключительными условиями.
В хорошо спроектированном коде более глубокие функции могут выдавать, а более высокие функции могут зацепиться Но ключ в том, что многие функции «между ними» вообще не будут обременены обработкой исключительных условий. Они только должны быть «исключительными», что не означает, что они должны ловить.
источник
Я хотел бы добавить к этому обсуждению, что, начиная с C ++ 11 , это имеет большой смысл, если каждый
catch
блокrethrow
является исключением вплоть до того момента, когда он может / должен быть обработан. Таким образом, можно создать обратную трассировку . Поэтому я считаю, что предыдущие мнения частично устарели.Используйте
std::nested_exception
иstd::throw_with_nested
Здесь и здесь описано, как этого добиться, в StackOverflow .
Поскольку вы можете сделать это с любым производным классом исключений, вы можете добавить много информации к такой обратной трассировке! Вы также можете взглянуть на мой MWE на GitHub , где обратная трассировка будет выглядеть примерно так:
источник
Мне дали «возможность» спасти несколько проектов, и руководители заменили всю команду разработчиков, потому что в приложении было слишком много ошибок, а пользователи устали от проблем и ошибок. Все эти кодовые базы имели централизованную обработку ошибок на уровне приложения, как описано в ответе с наибольшим количеством голосов. Если этот ответ - лучшая практика, почему он не сработал и не позволил предыдущей команде разработчиков решить проблемы? Возможно, иногда это не работает? В ответах выше не указано, сколько времени разработчики тратят на исправление отдельных проблем. Если время для решения проблем является ключевым показателем, лучше использовать инструментальный код с блоками try..catch.
Как моя команда исправила проблемы без значительного изменения пользовательского интерфейса? Все просто, каждый метод был снабжен блокировкой try..catch, и все было зарегистрировано в точке сбоя с именем метода, значения параметров метода объединены в строку, переданную вместе с сообщением об ошибке, сообщением об ошибке, именем приложения, датой, и версия. С помощью этой информации разработчики могут запускать аналитику на наличие ошибок, чтобы выявить исключение, которое возникает чаще всего! Или пространство имен с наибольшим количеством ошибок. Он также может проверить, что ошибка, возникающая в модуле, правильно обрабатывается и не вызвана несколькими причинами.
Еще одним преимуществом этого является то, что разработчики могут установить одну точку останова в методе регистрации ошибок, и с одной точкой останова и одним нажатием кнопки отладки «выйти» они находятся в методе, который не удался с полным доступом к фактическому объекты в точке сбоя, удобно доступны в ближайшем окне. Это делает его очень простым для отладки и позволяет перетаскивать выполнение обратно в начало метода, чтобы дублировать проблему, чтобы найти точную строку. Позволяет ли централизованная обработка исключений разработчику воспроизвести исключение за 30 секунд? Нет.
Утверждение «Метод должен перехватывать исключение только тогда, когда он может обрабатывать его каким-либо разумным способом». Это подразумевает, что разработчики могут предсказать или столкнутся с каждой ошибкой, которая может произойти до выпуска. Если бы это был действительно верхний уровень, обработчик исключений приложения не понадобился бы, и не было бы рынка для Elastic Search и logstash.
Этот подход также позволяет разработчикам находить и устранять периодически возникающие проблемы на производстве! Хотите отладку без отладчика в производстве? Или вы предпочитаете принимать звонки и получать электронные письма от расстроенных пользователей? Это позволяет вам решать проблемы до того, как об этом узнает кто-либо другой, и без необходимости отправлять электронные письма, мгновенные сообщения или сообщения в службу поддержки, так как все, что нужно для устранения проблемы, уже здесь. 95% проблем никогда не нужно воспроизводить.
Для правильной работы его необходимо объединить с централизованным ведением журнала, который может захватывать пространство имен / модуль, имя класса, метод, входные данные и сообщение об ошибке и сохранять их в базе данных, чтобы его можно было объединить, чтобы выделить наиболее неудачный метод, чтобы его можно было исправлено первым.
Иногда разработчики предпочитают выбрасывать исключения в стек из блока catch, но этот подход в 100 раз медленнее, чем обычный код, который не генерирует. Поймать и отпустить с регистрации является предпочтительным.
Этот метод использовался для быстрой стабилизации приложения, которое каждый час давало сбой большинству пользователей в компании Fortune 500, разработанной 12 разработчиками в течение 2 лет. Используя эти 3000 различных исключений, были выявлены, исправлены, протестированы и развернуты в течение 4 месяцев. Это усредняется для исправления каждые 15 минут в среднем в течение 4 месяцев.
Я согласен с тем, что вводить в код все, что нужно для инструментального кода, неинтересно, и я предпочитаю не смотреть на повторяющийся код, но добавление 4 строк кода к каждому методу в конечном итоге того стоит.
источник
Помимо приведенного выше совета, лично я использую некоторые try + catch + throw; по следующей причине:
источник
Я чувствую себя обязанным добавить еще один ответ, хотя в ответе Майка Уитя довольно хорошо суммированы основные моменты. Я думаю об этом так. Когда у вас есть методы, которые делают несколько вещей, вы умножаете сложность, а не добавляете ее.
Другими словами, метод, заключенный в попытку, имеет два возможных результата. У вас есть результат без исключения и результат исключения. Когда вы имеете дело со многими методами, это экспоненциально взрывается за пределами понимания.
Экспоненциально, потому что если каждый метод разветвляется двумя различными способами, то каждый раз, когда вы вызываете другой метод, вы возводите в квадрат предыдущее число потенциальных результатов. К тому времени, как вы вызвали пять методов, вы получите до 256 возможных результатов как минимум. Сравните это с тем, что в каждом отдельном методе не выполняется try / catch, и у вас есть только один путь.
Вот как я на это смотрю. Вы можете быть склонны возразить, что любой тип ветвления делает то же самое, но try / catches - особый случай, потому что состояние приложения в основном становится неопределенным.
Короче говоря, try / catches усложняет понимание кода.
источник
Вам не нужно скрывать каждую часть вашего кода внутри
try-catch
. Основное использованиеtry-catch
блока - обработка ошибок и получение ошибок / исключений в вашей программе. Некоторое использованиеtry-catch
-try-catch
блок.источник
try
/catch
полностью отделен / ортогональн от этого. Если вы хотите расположить объекты в меньшем объеме, вы можете просто открыть новый{ Block likeThis; /* <- that object is destroyed here -> */ }
- не нужно оборачивать это вtry
/,catch
еслиcatch
, конечно, вам ничего не нужно .