JavaScript: Исключения. Try… catch VS if…else

Данная статья расчитана для двух типов разработчиков:
- кто еще не знаком с исключениями
- кто пытается найти более разумное использование исключениям

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

Эволюция языков программирования приводит иногда к кардинальным изменениям в мировоззрении разработчиков. В мире РНР такое случилось при появлении пятой ветки, которая принесла новую объектную модель, новые наборы встроенных функций и новые методы обработки ошибок…

Те, кто начинал знакомиться с PHP (тут и далее я буду подразумевать пятую версию), после другого процедурного языка программирования, так и не поняли что же такого в переходе от 4ки к 5ке и продолжают оформлять код привычными функциями, которые находятся на одном уровне по отношению друг к другу, а так же каждое действие проверяют на удачный код возврата. Но те кто знали об исключениях и классах…

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

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

throw new Exception(\\\"This is exception message\\\", $exception_code);

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

try {
throw new Exception(\\\"Exception message\\\");
echo \\\"That code will never been executed\\\";
} catch (Exception $e) {
echo $e->getMessage(); //выведет \\\"Exception message\\\"
}

Как видно из примера, при выбрасывании исключения, остальной код в блоке try выполнен не будет, а управление будет передано в оператор catch, в котором мы указываем, как будет называться объект, в который будет передано выброшенное исключение (в нашем случае - $e). Внутри блока оператора catch, на основании данных из исключения мы можем применять какое-либо действие в зависимости от ситуации. Сейчас мы просто вывели сообщение, которое было передано исключением.

Объект Exception (который кстати можно унаследовать) имеет следующий набор финальных (final) методов:

final function getMessage(); // сообщение исключения
final function getCode(); // код исключения
final function getFile(); // файл из которого брошено исключение
final function getLine(); // строка бросания
final function getTrace(); // массив стека вызовов
final function getTraceAsString(); // массив стека вызовов отформатированый в строку
Практика Когда же удобно использовать исключения? Да всегда, когда функция или метод может прийти к ошибочной ситуации! На примере встроенной функции mysql_connect() и mysql_select_db(), если бы они бросала исключения при ошибке соединения и выборе базы данных соответственно, код выглядел бы так:
try {
mysql_connect($hostname, $username, $password);
mysql_select_db($dbname);
} catch (Exception $e) {
echo $e->getMessage(); //выведет либо сообщение об ошибке подключения, либо об ошибке выбора
}

И в зависимости от результата мы бы:

* удачно присоединились бы к СУБД и выбрали БД (код в сatch блоке не выполнился бы)
* при неудаче соединения с СУБД, вывели бы соответствующую ошибку и прикатили выполнение сеанса
* при ошибке выбора БД, оповестили бы пользователя об этой неприятности

Теперь самое время спросить: \\\«А зачем нам такая сложная штука, если можно использовать оператор if?\\\».

Первый же ответ: \\\«Потому что так код более читабельный, а обработка ошибок выведена в отдельный блок\\\». Если бы мы использовали оператор if, то код выглядел бы следующим образом:

$connId = mysql_connect($hostname, $username, $password);
if (false == $connId) {
echo \\\"Ошибка подключения к СУБД\\\";
}

$flag = mysql_select_db($dbname);
if (false == $flag) {
echo \\\"Невозможно выбрать базу данных.\\\";
}

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

class MyException extends Exception {}

Try {
try {
//...
throw new Exception(\\\"inner\\\");
//...
} catch (Exception $e) {
throw new MyException(\\\"outer\\\");
}
} catch (MyException $e) {
echo $e->getMessage(); //выведет \\\"outer\\\"
}

Производительность Пытаясь достичь истины, провел несколько экспериментов с различными типами функций.
Первый тип возвращал статус true и проверялся операторами if...else
Второй тип возвращал статус false и проверялся операторами if...else
Третий тип просто выполнял действия и ничего не возвращал. Проверялся блоком try...catch
Четвертый тип всегда бросал исключение и проверялся в try...catch

Результаты:

True: 0.72382092475891
False: 0.85190796852112
No exception: 0.72565317153931
Exception: 14.176206827164

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

ВыводыКогда же использовать исключения? Всегда, когда подразумевается ошибка или нестандартное поведение программы, а так же когда принятие решения об обработке результата необходимо переложить на более высокий уровень.
А что же делать с оператором if и булевыми статусами отработки функций? Оставить их. Но толко там где они действительно необходимы. Тоесть там, где логический оператор подразумевает использование результата в вычислениях, а не контроле потока выполнения. Тоесть все удачные завершения функций теперь не нужно оповещать оператором return true, если это логическое значение не пригодиться для дальнейших вычислений. И в то же время все статусы завершения функций с ошибками изменить из формата return false в формат throw new Exception()
Что нужно помнить? Использование исключений предполагает что весь код выполняется со статусом true (без ошибок), но если ошибка произошла, то для ее обработки всегда найдется место в блоке catch.

Операторы перехода и обработка исключений

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

Оператор return заставляет интерпретатор перейти из вызванной функции обратно в точку ее вызова и вернуть значение вызова. Оператор throw возбуждает исключение и предназначен для работы в сочетании с операторами try/catch/finally, которые определяют блок программного кода для обработки исключения. Это достаточно сложная разновидность операторов перехода: при появлении исключения интерпретатор переходит к ближайшему объемлющему обработчику исключений, который может находиться в той же функции или выше, в стеке возвратов вызванной функции.

Подробнее все эти операторы перехода описываются в следующих подразделах.

Метки инструкций

Любая инструкция может быть помечена указанным перед ней идентификатором и двоеточием:

идентификатор: инструкция

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

Присвоив имя циклу, его затем можно использовать в инструкциях break и continue, внутри цикла для выхода из него или для перехода в начало цикла, к следующей итерации. Инструкции break и continue являются единственными инструкциями в языке JavaScript, в которых можно указывать метки - о них подробнее рассказывается далее. Ниже приводится пример инструкции while с меткой и инструкции continue, использующей эту метку:

Mainloop: while (token != null) { // Программный код опущен... continue mainloop; // Переход к следующей итерации именованного цикла }

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

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

Оператор break

Оператор break приводит к немедленному выходу из самого внутреннего цикла или оператора switch. Ранее мы уже видели примеры использования оператора break внутри оператора switch. В циклах он обычно используется для немедленного выхода из цикла, когда по каким-либо причинам требуется завершить выполнение цикла.

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

Var arr = ["а","б","в","г","д"], result; for (var i = 0; i

В языке JavaScript допускается указывать имя метки за ключевым словом break (идентификатор без двоеточия):

break имя_метки;

Когда оператор break используется с меткой, она выполняет переход в конец именованной инструкции или прекращение ее выполнения. В случае отсутствия инструкции с указанной меткой попытка использовать такую форму оператора break порождает синтаксическую ошибку. Именованная инструкция не обязана быть циклом или оператором switch. Оператор break с меткой может выполнять «выход» из любой вмещающей ее инструкции. Объемлющая инструкция может даже быть простым блоком инструкций, заключенным в фигурные скобки исключительно с целью пометить его.

Между ключевым словом break и именем метки не допускается вставлять символ перевода строки. Дело в том, что интерпретатор JavaScript автоматически вставляет пропущенные точки с запятой: если разбить строку программного кода между ключевым словом break и следующей за ним меткой, интерпретатор предположит, что имелась в виду простая форма этого оператора без метки, и добавит точку с запятой.

Оператор break с меткой необходим, только когда требуется прервать выполнение инструкции, не являющейся ближайшим объемлющим циклом или оператором switch.

Оператор continue

Оператор continue схож с оператором break. Однако вместо выхода из цикла оператор continue запускает новую итерацию цикла. Синтаксис оператора continue столь же прост, как и синтаксис оператора break. Оператор continue может также использоваться с меткой.

Оператор continue, как в форме без метки, так и с меткой, может использоваться только в теле цикла. Использование его в любых других местах приводит к синтаксической ошибке. Когда выполняется оператор continue, текущая итерация цикла прерывается и начинается следующая. Для разных типов циклов это означает разное:

    В цикле while указанное в начале цикла выражение проверяется снова, и если оно равно true, тело цикла выполняется с начала.

    В цикле do/while происходит переход в конец цикла, где перед повторным выполнением цикла снова проверяется условие.

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

    В цикле for/in цикл начинается заново с присвоением указанной переменной имени следующего свойства.

Обратите внимание на различия в поведении оператора continue в циклах while и for. Цикл while возвращается непосредственно к своему условию, а цикл for сначала вычисляет выражение инкремента, а затем возвращается к условию. В следующем примере показано использование оператора continue без метки для выхода из текущей итерации цикла для четных чисел:

Var sum = 0; // Вычислить сумму не четных чисел от 0 - 10 for (var i = 0; i

Оператор continue, как и break, может применяться во вложенных циклах в форме, включающей метку, и тогда заново запускаемым циклом необязательно будет цикл, непосредственно содержащий оператор continue. Кроме того, как и для break, переводы строк между ключевым словом continue и именем метки не допускаются.

Оператор return

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

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

Оператор return может также использоваться без выражения, тогда она просто прерывает выполнение функции и возвращает значение undefined вызывающей программе. Например:

Function myFun(arr) { // Если массив содержит отриц. числа, прервать функцию for (var i = 0; i

Оператор throw

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

В JavaScript исключения возбуждаются в тех случаях, когда возникает ошибка времени выполнения и когда программа явно возбуждает его с помощью оператора throw. Исключения перехватываются с помощью операторов try/catch/finally, которые описываются позже.

Оператор throw имеет следующий синтаксис:

throw выражение;

Результатом выражения может быть значение любого типа. Оператору throw можно передать число, представляющее код ошибки, или строку, содержащую текст сообщения об ошибке. Интерпретатор JavaScript возбуждает исключения, используя экземпляр класса Error одного из его подклассов, и вы также можете использовать подобный подход. Объект Error имеет свойство name , определяющее тип ошибки, и свойство message , содержащее строку, переданную функции-конструктору. Ниже приводится пример функции, которая возбуждает объект Error при вызове с недопустимым аргументом:

// Функция факториала числа function factorial(number) { // Если входной аргумент не является допустимым значением, // возбуждается исключение! if (number 1; i *= number, number--); /* пустое тело цикла */ return i; } console.log("5! = ", factorial(5)); console.log("-3! = ", factorial(-3));

Когда возбуждается исключение, интерпретатор JavaScript немедленно прерывает нормальное выполнение программы и переходит к ближайшему обработчику исключений. В обработчиках исключений используется оператор catch конструкции try/catch/finally, описание которой приведено в следующем разделе.

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

Если исключение генерируется в функции, не содержащей конструкции try/catch/finally, предназначенной для его обработки, то исключение распространяется выше, в программный код, вызвавший функцию. Таким образом исключения распространяются по лексической структуре методов JavaScript вверх по стеку вызовов. Если обработчик исключения так и не будет найден, исключение рассматривается как ошибка и о ней сообщается пользователю.

Конструкция try/catch/finally

Конструкция try/catch/finally реализует механизм обработки исключений в JavaScript. Оператор try в этой конструкции просто определяет блок кода, в котором обрабатываются исключения. За блоком try следует оператор catch с блоком инструкций, вызываемых, если где-либо в блоке try возникает исключение. За оператором catch следует блок finally , содержащий программный код, выполняющий заключительные операции, который гарантированно выполняется независимо от того, что происходит в блоке try.

И блок catch, и блок finally не являются обязательными, однако после блока try должен обязательно присутствовать хотя бы один из них. Блоки try, catch и finally начинаются и заканчиваются фигурными скобками. Это обязательная часть синтаксиса, и она не может быть опущена, даже если между ними содержится только одна инструкция.

Следующий фрагмент иллюстрирует синтаксис и назначение конструкции try/catch/finally:

Try { // Обычно этот код без сбоев работает от начала до конца. // Но в какой-то момент в нем может быть сгенерировано исключение // либо непосредственно с помощью оператора throw, либо косвенно - // вызовом метода, генерирующего исключение. } catch (ex) { // Инструкции в этом блоке выполняются тогда и только тогда, когда в блоке try // возникает исключение. Эти инструкции могут использовать локальную переменную ex, // ссылающуюся на объект Error или на другое значение, указанное в операторе throw. // Этот блок может либо некоторым образом обработать исключение, либо // проигнорировать его, делая что то другое, либо заново сгенерировать // исключение с помощью оператора throw. } finally { // Этот блок содержит инструкции, которые выполняются всегда, независимо от того, // что произошло в блоке try. Они выполняются, если блок try завершился: // 1) как обычно, достигнув конца блока // 2) из-за операторов break, continue или return // 3) с исключением, обработанным приведенным в блоке catch выше // 4) с неперехваченным исключением, которое продолжает свое // распространение на более высокие уровни }

Обратите внимание, что за ключевым словом catch следует идентификатор в скобках. Этот идентификатор похож на параметр функции. Когда будет перехвачено исключение, этому параметру будет присвоено исключение (например, объект Error). В отличие от обычной переменной идентификатор, ассоциированный с оператором catch, существует только в теле блока catch.

Далее приводится более реалистичный пример конструкции try/catch. В нем вызываются метод factorial(), определенный в предыдущем примере, и методы prompt() и alert() клиентского JavaScript для организации ввода и вывода:

Try { // Запросить число у пользователя var n = Number(prompt("Введите положительное число", "")); // Вычислить факториал числа, предполагая, // что входные данные корректны var f = factorial(n); // Вывести результат alert(n + "! = " + f); } catch (ex) { // Если данные некорректны, управление будет передано сюда alert(ex); // Сообщить пользователю об ошибке }

Если пользователь введет отрицательное число, высветится предупреждающее сообщение:

Это пример конструкции try/catch без оператора finally. Хотя finally используется не так часто, как catch, тем не менее иногда этот оператор оказывается полезным. Блок finally гарантированно исполняется, если исполнялась хотя бы какая-то часть блока try, независимо от того, каким образом завершилось выполнение программного кода в блоке try. Эта возможность обычно используется для выполнения заключительных операций после выполнения программного кода в продолжении try.

В обычной ситуации управление доходит до конца блока try, а затем переходит к блоку finally, который выполняет необходимые заключительные операции. Если управление вышло из блока try как результат выполнения операторов return, continue или break, перед передачей управления в другое место выполняется блок finally.

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

Если сам блок finally передает управление с помощью операторов return, continue, break или throw или путем вызова метода, генерирующего исключение, незаконченная команда на передачу управления отменяется и выполняется новая. Например, если блок finally сгенерирует исключение, это исключение заменит любое ранее сгенерированное исключение.

Конструкция try...catch позволяет вам проверять блок кода на наличие ошибок.

JavaScript - Перехват Ошибок

При просмотре Веб страниц в интернете все мы сталкивались с сигнальным окном JavaScript, говорящим нам, что произошла ошибка времени выполнения и спрашивающим "Не желаете ли перейти к отладке?". Сообщение об ошибке навроде этого может быть полезно для разработчиков, но не для пользователей. Когда пользователи видят ошибки, они как правило покидают Веб страницу.

Этот урок научит вас, как перехватывать и обрабатывать сообщения об ошибках JavaScript, так что вы не потеряете ваших зрителей.

Конструкция try ... catch

Конструкция try ... catch позволяет вам проверять блок кода на наличие ошибок. Блок try содержит код, который будет исполняться и проверяться, а блок catch содержит код, который будет выполнен при возникновении ошибок.

Синтаксис
try
{
//Запустить некоторый код здесь
}
catch(err)
{
//Обработка ошибок здесь
}

Заметьте, что try ... catch пишется в нижнем регистре (маленькими буквами). Использование заглавных букв сгенерирует ошибку JavaScript!

Примеры

Пример ниже, как предполагается, выведет всплывающее окно "Добро пожаловать гость!" при нажатии кнопки мыши. Однако, есть опечатка в функции message(). alert() напечатано как adddlert(). Возникает ошибка JavaScript. Блок catch перехватывает ошибку и выполняет дополнительный код для ее обработки. Код отображает пользовательское сообщение об ошибке, информирующее пользователя, что произошло:

Следующий пример использует окно подтверждения, чтобы показать пользовательское сообщение, говорящее пользователям, что они могут нажать OK, чтобы продолжить, или нажать Отмена, чтобы перейти на главную страницу. Если метод возвращает false, пользователь нажал Отмена, и код перенаправляет пользователя. Если же метод возвращает true, код ничего не делает:

Пример



var txt="";
function message()
{
try
{

}
catch(err)
{

If(!confirm(txt))
{
document.location.href="http://сайт/";
}
}
}


In this example, we have made a typo in the code (in the try block ).

The example is supposed to alert "Welcome guest!", but alert is misspelled.

The catch block catches the error, and executes a code to handle it:




try {
adddlert("Welcome guest!");
}
catch(err) {
document.getElementById("demo").innerHTML = err.message;
}

More "Try it Yourself" examples below.

Definition and Usage

The try/catch/finally statement handles some or all of the errors that may occur in a block of code, while still running code.

Errors can be coding errors made by the programmer, errors due to wrong input, and other unforeseeable things.

The try statement allows you to define a block of code to be tested for errors while it is being executed.

The catch statement allows you to define a block of code to be executed, if an error occurs in the try block.

The finally statement lets you execute code, after try and catch, regardless of the result.

Note: The catch and finally statements are both optional, but you need to use one of them (if not both) while using the try statement.

Tip: When an error occurs, JavaScript will normally stop, and generate an error message. Use the throw statement to create a custom error (throw an exception). If you use throw together with try and catch , you can control program flow and generate custom error messages.

For more information about JavaScript errors, read out JavaScript Errors Tutorial.

Browser Support Statement
try/catch/finally Yes Yes Yes Yes Yes
Syntax

Try {
tryCode - Block of code to try
}
catch(err ) {
catchCode - Block of code to handle errors
}
finally {
finallyCode - Block of code to be executed regardless of the try / catch result
}

Parameter Values Parameter Description
tryCode Required. Block of code to be tested for errors while it is being executed
err Required if used with catch. Specifies a local variable that refers to the error. The variable can refer to the Error object (contains information about the occurred error, like the message ""addlert" is not defined"). If the exception was created by the throw statement, the variable refers to the object specified in the throw statement (see "More Examples")
catchCode Optional. Block of code to be executed, if an error occurs in the try block. If no error occurs, this block of code is never executed
finallyCode Optional. Block of code to be executed regardless of the try / catch result
Technical Details JavaScript Version:
ECMAScript 3
More Examples Example

This example examines input. If the value is wrong, an exception (err) is thrown.

The exception (err) is caught by the catch statement and a custom error message is displayed:



Please input a number between 5 and 10:


Test Input


function myFunction() {
var message, x;
message = document.getElementById("message");
message.innerHTML = "";
x = document.getElementById("demo").value;
try {
if(x == "") throw "is Empty";
if(isNaN(x)) throw "not a number";
if(x > 10) throw "too high";
if(x < 5) throw "too low";
}
catch(err) {
message.innerHTML = "Input " + err;
}
}

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

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

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

  • ввод исходных данных;
  • вычисления по определенным формулам;
  • переход по какой-то ветви по определенному условию;
  • проход определенной итерации цикла;
  • выход из цикла и т. д.

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

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

char *Buffer = new char ;

if (Buffer = = 0)

throw "out of memory"; //нехватка памяти

Если исключение сгенерировано, но в программе не предусмотрена его обработка, механизм исключений вызывает из стандартной динамической библиотеки языка C++ функцию завершения terminate, которая выдает сообщение «abnormal program termination», которое не сообщает никаких сведений о причине и месте прерывания программы и прекращает выполнение программы.

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

{ //начало контролируемого блока

//операторы ...

char *Buffer = new char ; if (Buffer = = 0)

throw "out of memory";

//генерация исключения с параметром - строкой

//операторы...

} //конец контролируемого блока

//обработка исключения

catch (char *ErrorMsg) /^определение функции обработки ошибки, параметром которой является любая строка char *ErrorMsg*/

cout « ErrorMsg « " "; /* вывод сообщения об ошибке (обработка

ошибки) и вызов диалогового окна для возобновления выполнения программы или вызов функции exit

} //конец блока обработки исключений контролируемого блока

/*здесь выполнение программы продолжается, если не произошло прерывание

работы программы в контролируемом блоке... */

Если исключение генерируется в любом месте контролируемого блока, следующем за оператором try (или внутри любой функции в этом блоке), то управление передается за пределы блока try. Если за try следует подходящий блок catch, то управление переходит к нему. Блок catch начинается с объявления в круглых скобках. Если тип параметра в этом объявлении совпадает с типом значения в операторе throw, генерирующем исключение, то управление передается данному блоку catch. При несовпадении типов параметров программа ищет другой обработчик catch, тип параметра которого совпадает с типом параметра исключения, и т. д. до конца блока обработки исключений контролируемого блока. После выполнения кода в блоке catch управление передается первому оператору, следующему за блоком обработки исключений, и программа возобновляет работу в нормальном режиме (если блок catch не содержит оператор return или вызов функции exit). Таким образом, операторы try и catch могут предотвратить завершение программы стандартным обработчиком исключений, заменив обработку исключений специальным, понятным программистам способом с указанием места и причины исключения.

Блок try называют охраняемым (контролируемым) разделом кода. Если исключение не сгенерировано, то поток управления «перепрыгнет» все блоки catch и перейдет на первый, следующий за блоком оператор.

В приведенном выше примере оператор throw содержит строку «out of memory». Так как тип параметра в объявлении catch (char *) такой же, то данный блок catch получает управление при вызове этого исключения.

Обратите внимание: оператор catch объявляет параметр типа char * с именем ErrorMsg, что позволяет внутри блока catch получить доступ к значению, заданному в throw. Данный механизм очень

похож на механизм передачи параметров функции. Чтобы понять это, можно представить оператор throw как вызов функции:

throw "out of memory";,

в котором значение («out of memory») передается в функцию.

Также можно представить блок catch как вызываемую функцию, а объявление char * Errormsg как объявление ее формального параметра. Как и параметры функции, переменная ErrorMsg доступна только внутри catch.

Заметьте: в операторе catch может содержаться только описание типа без имени параметра.

//не использует значение, определенное в операторе throw

В этом случае блок catch, как и прежде, получит управление по оператору throw " out of memory", но без доступа к значению типа

Если оператор catch определяет какой-либо другой тип параметра (например, int), блок не получит управление при возникновении данного исключения.

catch (int ErrorCode)

/*управление НЕ будет получено после выполнения оператора throw "out of memory"*/

Если поместить несколько catch за блоком try, то можно управлять различными типами исключений. Например, в следующем фрагменте программы обрабатываются исключения с аргументом типа char * или int.

char *Bufferl = new char ; /*попытка выделить память

под WOO символов и записать указатель на эту область памяти

в Buf f erl*/

if (Bufferl = = 0)

throw "out of memory Bufferl"; /* генерация исключения

с параметром-строкой - это исключение обрабатывается

первым блоком catch V

char *Buffer2 = new char ; /^попытка выделить память

под 1000 символов и записать указатель на эту область памяти в Buf f ег2 */

if (Buffer2 = = 0)

//если память не удалось выделить, генерируем исключение throw "out of memory Buffer2"; /*генерация исключения

с параметром-строкой это исключение обрабатывается

первым блоком catch V

for (i=0; iclOOO; i++) if (Buffer2[i]==0)

throw i; //это исключение обрабатывается вторым блоком catch Bufferl[і]=Bufferl[і]/Buffer2[i];

//обработка исключений

catch (char *ErrorMsg) /*определение функции обработки ошибки, параметром которой является любая строка char *ErrorMsg*/

cout « ErrorMsg « " "; exit для остановки работы программы*/

catch (int ErrorCode) //обработка любого исключения типа int { cout «"Арифметическая ошибка при i - "«ErrorCode«endl;

/*вывод сообщения об ошибке (обработка ошибки) и вызов диалогового окна для возобновления выполнения программы или вызов функции exit для остановки работы программы* /

Если объявление блока catch содержит многоточие, то он получает управление в ответ на исключение любого типа, сгенерированное предыдущими блоками try.

//получает управление в ответ на исключения любого типа

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