Программирование >>  Аргументация конструирования 

1 ... 93 94 95 [ 96 ] 97 98 99 ... 108


Глбш27

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

/S atfiou главе...

v Зачем нужен новый механизм обработки ошибок

Механизм исключительных ситуаций Так что же мы будем бросать?

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

Механизм исключительных ситуаций базируется на ключевых словах try

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

(перехватить) вызывающая функция.

Это продемонстрировано в приведенном ниже фрагменте. tinclude <io3tredm.h>

factorial - вычисляет факториал int factorial(int n)

Поскольку функция не может работатв с

ельными п, мы сразу проверяем допустимость переданного аргумента if (л < О)

throw ОтрицателБный аргумент ;

теперь вычислим факториал

int accum = 1; whiL=(n > 0]

эссшп *= п; л-~;

return accum;

in (int argcs, char* pArgs [ ] ) {



/ / Эта строка генерирует исключение cout Factorial of -1 is < ial (-1) endl;

Управление никогда не дойдет до этой строки cout Factorial of 10 is factorial(10) endl;

Управление будет передано сюда catch (char* pError) {

cout Возникла ошибка: pError endl;

return 0;

начинается с блока, выделенного ключевым словом try. В этом блоке выполнение кода ничем не отличается от выполнения вне блока. В данном слу-ча Min () пытается вычислить факториал отрицательного числа. Однако функцию factorial (} не так легко одурачить, поскольку она достаточно умно написана и обнаруживает, что запрос некорректен. При этом она генерирует сообщение об ошибке с помощью ключевого слова throw. Управление передается фрагменту, находящемуся

сразу за закрывающей фигурной скобкой блока try и отвечающему за перехват сообщения об ошибке.

Зачежт новыймеханизм oSfuiSof§ucu ошибок

Что плохого в методе возврата ошибки, подобном применяемому в FORTRAN? Факториал не может быть отрицательным, поэтому я мог бы сказать что-то вроде: Если функция factorial () обнаруживает ошибку, она возвращает отрицательное число. Значение отрицательного числа будет указывать на источник проблемы . Чем же плох такой метод? Ведь так бьшо всегда.

К сожалению, здесь возникает несколько проблем. хотя результат фак-

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

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

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

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

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



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

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

errRtn = someFunc(); if (errRtn)

errorOut{ Ошибка при вызове someFn() }; return MY ERR0R 1

errRtn = someOtherFunc() ;

if (errRtn)

errorOut( Ошибка при вызове someOtherFn() ); return My ERROR l

Такой механизм имеет ряд недостатков. К Изобилует новторениями.

Заставляет программиста отслеживать множество разных ошибок и I сать код для обработки всех возможных вариантов.

Смешивает код, отвечающий за обработку с обычным кодом,

что не добавляет ясности программе...

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

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

C++ не найдет функцию, которая обработает возникшую проблему. Это также дает

возможность игнорировать ошибки, которые вы не в состоянии обработать.

Механизмиасмони/Яем/шх сиЩсщии

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

Если блок try в данной функции не найден, управление передается вызывающей

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

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

бы функция выполнила команду return. Это оберегает программу от потери ресурсов

и праздно щатающихся неуничтоженных объектов.

Когда необходимый блок try найден, программа ищет первый блок catch

(который должен находиться сразу за закрывающей скобкой блока try). Если тип

сгенерированного объекта совпадает с типом аргумента, указанным в блоке catch,



1 ... 93 94 95 [ 96 ] 97 98 99 ... 108

© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика