Использование блоков try/catch в С++
В общем, я стараюсь использовать try/catch для кода, который имеет несколько точек отказа, для которых ошибки имеют общий обработчик.
По моему опыту, это обычно код, который квалифицирует ввод или контекст перед выполнением какого-либо действия или вывода после выполнения некоторых действий.
Я получил совет от литературы и коллег, чтобы свести к минимуму код в таких блоках, и я принимаю это как обычно добрые советы.
Я хотел бы узнать немного больше об основах вышеупомянутого совета:
- Какова природа накладных расходов?
- Есть ли недавние рекомендации по разработке, которые касаются рекомендуемого использования (или избежания) блоков try/catch?
- Насколько быстрые процессоры и более современные компиляторы смягчают проблемы с try/catch?
Заранее спасибо за помощь,
AJ
Ответы
Ответ 1
В С++ стоимость зависит от реализации. В общем случае существует два способа реализации исключений:
Первый - это "табличный" подход. Компилятор строит набор таблиц для поиска, в том месте, где выбрано исключение, куда идти. Когда генерируется исключение, он должен искать каждую таблицу в стеке вызовов до тех пор, пока не найдет что-то, что поймает это исключение. Так как это все время выполнения, вход или выход из try catch не производит штраф (хороший), но бросание исключения включает в себя потенциально много запросов, создающих гораздо более медленный бросок. Я лично предпочитаю неплатежи за блокировку catch, потому что исключения должны быть очень редким обстоятельством. Это также сделало бы исполняемые файлы более крупными, если они должны были хранить таблицы.
Секунды - это "кодовый" подход. Каждый раз, когда код входит в блок catch try, концептуально расположение блока помещается в стек. Это приводит к стоимости при входе и выходе из блока try-catch, однако при возникновении исключения механизм выполнения может быстро выскочить из стека, чтобы найти, куда идти. Итак, бросание исключений (намного?) Быстрее, но вход в блок теперь имеет стоимость. Помещение блока catch try в замкнутом петле низкого уровня может привести к значительным накладным расходам.
Вам нужно будет проверить свой конкретный компилятор, чтобы узнать, какой из них он использует.
Ответ 2
Я нашел технический отчет о производительности С++ (pdf warning), который включает раздел об исключениях. Вам это может показаться интересным. У меня были коллеги, которые считали, что на каждой инструкции в блоке try/catch есть накладные расходы, но этот технический отчет, похоже, не поддерживает эту идею.
Ответ 3
К вашему второму вопросу: общие рекомендации здесь,
Herb Sutter также дает неплохой совет здесь.
Ответ 4
Зависит от компилятора. Почему бы вам не написать простую функцию с блоком try-catch и аналогичным без него и сравнить сгенерированный машинный код?
Ответ 5
Я нахожу сайт С++ часто задаваемых вопросов, и соответствующая книга имеет просветительскую дискуссию по этому вопросу.
http://www.parashift.com/c++-faq-lite/exceptions.html
Ответ 6
По моему опыту, самая большая проблема с блоками try/catch заключается в том, что мы часто пытаемся поймать исключения в общих чертах. Например, если я обернул свою основную функцию блоком try/catch, который ловит (...), я в основном пытаюсь не разрешить моей программе сбой b/c отброшенного исключения.
Проблема с этим подходом, как я вижу, в два раза. 1) Пока я тестирую и отлаживаю, я не вижу никаких ошибок, и у меня нет возможности исправить их. 2) Это действительно вроде ленивый выход. Вместо того, чтобы думать о проблемах, которые могут возникнуть, и выяснить, что такое край, я просто пытаюсь не провалиться. Попытка не терпеть неудачу - это многое другое, чем попытка добиться успеха.
Ответ 7
В большинстве языков, входящих и выходящих из блока try/catch через обычные методы, бесплатно, это только тогда, когда создается исключение, которое обработчик исключений ищет, где обрабатывать исключение.
Ответ 8
В С++ вы не должны использовать блоки try/catch для выполнения очистки. Вместо этого вы можете использовать шаблоны для выполнения инвентаризации ресурсов.
auto_ptr - это один [плохой пример]
Блокировки синхронизации, в которых вы храните мьютекс в качестве переменной состояния и используете локальную переменную (шаблоны или обычные классы) для выполнения методов .acquire()/. release().
Чем больше вы это делаете, тем меньше вам приходится беспокоиться о том, чтобы вручную освобождать объекты в исключительных условиях. Компилятор С++ сделает это за вас.