В некоторой степени ваши вопросы зависят от вашей конкретной операционной среды и языка (на который вы намекаете, но никогда не раскрываете), но в некоторой степени проблемы носят общий характер.(К сожалению, это также область разногласий, как между различными операционными средами, так и между отдельными разработчиками, иногда в одном и том же проекте, поэтому то, что я говорю ниже, может потребоваться изменить для вашего контекста.)
Проработав с исключениями более 30 лет, я видел практически все механизмы и стили исключений, в том числе очень примитивные, а некоторые слишком «милые».Однако есть несколько общих принципов:
Во-первых, необходимо определить, что является исключением.Для наших целей, скажем, это уведомление о необычном условии, которое связано с кодом, выполняемым в настоящее время (а не, скажем, с условием на устройстве ввода-вывода, которое текущий код не использует).И скажем, он доставляется синхронно относительно текущего исполняемого кода, не задерживается (за исключением, может быть, нескольких машинных циклов) и не (изначально) отправляется в несвязанный код.
Продолжаем в этомВ частности, мы предполагаем, что текущая исполняемая программа имеет (по крайней мере концептуальный) стек возврата-вызова, представляющий порядок, в котором вызовы были вложены с начала выполнения программы до точки исключения.Поскольку это так, и поскольку исключения «связаны» с исполняемым в данный момент кодом (например, деление на ноль происходит из-за операции деления в текущей процедуре), также существует своего рода связь с каждой процедурой встек обратного вызова.То есть каждая процедура «знает», что это исключение прямо или косвенно возникло в результате ее собственного выполнения и вызванных процедур.
Что это говорит?Ну, обычно процедура, ближайшая к ошибке (т. Е. Та, что находится на «вершине» стека вызова-возврата), лучше всего «знает», какова причина ошибки, и лучше всего подходит для ее устранения.Но иногда (например, исключение «файл не найден») процедура, выполняющаяся ближе всего к точке исключения, не обладает достаточными знаниями для обработки ошибки (потому что, например, она не знает, где вызывающая сторона получила несуществующий файлимя или то, что содержимое файла должно было представлять).По этой причине, если текущая процедура не может обработать ошибку, она должна (неявно или явно) «повторно направить» ошибку вызывающей процедуре (и т. Д. Вниз по стеку вызова-возврата, пока не будет найдена процедура, которая хочет обработать ошибку).).Большинство схем исключений делают это «отставление» автоматически, хотя некоторые требуют, чтобы программист управлял им в определенной степени.
В конце концов, если ошибка не обрабатывается, вы попадаете в конец стека вызова-возврата.Это когда обработчик по умолчанию вступает во владение и решает, что делать (убить программу, возобновить с некоторой «безопасной» точки перезапуска в коде и т. Д.).Обработчик по умолчанию также, вероятно, попытается зарегистрировать ошибку (и содержимое стека возврата-возврата), чтобы ее можно было позже отладить.
Итак (при отсутствии каких-либо причин, не связанных с вашей конкретной операционной средой),Лучший способ обработки исключений - это отдельные процедуры, которые лучше всего «знают», что они означают.В некоторых случаях процедура может знать только, что означает исключение в общем смысле (например, по какой-то причине, что деление на ноль означает «недопустимый вес»), поэтому она может уйти в отставку с новым, определенным программой типом исключения, ожидая, чтовызывающая сторона (или вызывающая сторона ...) будет «знать», что означает «недопустимый вес» и как с этим справиться).
Многие люди считают, что исключения должны использоваться только для обработки «катастрофических» ошибок, которые обязательно приведут по крайней мере к неприятному сообщению об ошибке пользователю, если не к полному завершению программы, но это представление совершенно неверно.Совершенно нормально использовать исключения для управления, скажем, недопустимым пользовательским вводом, и, на самом деле, обычно лучше использовать исключения с точки зрения «структурированного программирования», чем использовать коды возврата и тому подобное.Единственное предостережение заключается в том, что в некоторых средах исключения являются болезненно медленными и не должны использоваться там, где они могут возникать со значительной частотой.Но большинство новых сред более «просвещены», и в них исключения могут использоваться без особой заботы о производительности.
При использовании исключений нужно немного изменить стиль программирования.Самое главное, нужно позаботиться о том, чтобы структурировать код так, чтобы маркеры начала / конца для диапазона обработки исключений соответствовали началу и концу концептуальных операций, связанных с этим возможным исключением, и нужно быть уверенным, что любой код «возврата»необходимо отменить частично завершенную операцию в случае, если исключение соответствующим образом связано с этим диапазоном исключений (например, с предложением "finally" или, если язык не поддерживает это, в качестве отдельной подпрограммы, которая может быть вызвана изобработчик исключений для диапазона исключений).Но как только вы начинаете понимать такие вещи, восстановление после ошибок становится на намного, намного проще.
В общей схеме вещей вполне нормально не обрабатывать исключения, которые не могут бытьобработать, и пусть обработчик по умолчанию позаботится о них.Однако в некоторых случаях может потребоваться, например, закрыть файлы или освободить ресурсы.Иногда это можно сделать из обработчика по умолчанию, но в других случаях его лучше обрабатывать с использованием отдельных обработчиков исключений (обычно настраиваемых на перехват «любых» исключений), которые просто выполняют необходимые операции очистки и затем повторно отправляют исходное исключение.Исключения позволяют вам делать это рядом с местом в коде, где ресурс был выделен в первую очередь, так что нет необходимости, чтобы обработчик по умолчанию содержал большой список всех выделенных ресурсов и знал, как освободить их все,Этот метод может значительно уменьшить количество ошибок из-за неосвобожденных ресурсов.
«Разматывание стека (трассировка)» может иметь несколько различных значений, но, как правило, когда исключение возникает в процедуре A, но повторяется через вызов- вернуть стек вызывающему A B, а затем, скажем, B вызывающему C, а обработчик исключений C «обрабатывает» исключение (не передает его вызывающему C), необходимо восстановить стек возврата-возврата в состояние, в котором онбыл во время выполнения C, так что C мог продолжить «нормальное» (более или менее) выполнение.Для этого записи для A и B в стеке будут (по крайней мере концептуально) «вытолкнутыми» или «раскрученными».Иногда (в зависимости от системы / языка) это «раскручивание» происходит, когда происходит «пульсация» необработанного исключения, но иногда этого не происходит, пока исключение не будет помечено как «обработанное».Обычно вам не нужно беспокоиться об этом, но некоторые системы или некоторые особые случаи могут потребовать знания об этом.
Также полезно отметить, что большинство систем создают содержимое стека возврата-вызова ввремя исключения доступно каким-либо образом для регистрации в качестве отладочной информации.Это чрезвычайно полезно, особенно при отладке «удаленных» ошибок, и стоит выяснить, как механизм работает в вашей системе.