Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 51
Текст из файла (страница 51)
Например: оо!с)М)пе Я')) //осссибка Я) не объявлена в Мспе ( 230 Глава 8. Пространства имен и исключения 8.3. Исключения Когда программа конструируется из раздельных модулей, и, особенно, когда эти модули находятся в независилю разработанных библиотеках, обработка ошибок должна быть разделена на две части: (1( генерация информации о возникновении опшбочной ситуации, которая не может бьгп разрешена локально; (2( обработка оцшбок, обнару>кеннгях в других местах.
Автор библиотеки может обнаружить ошибки во время выполнения, но, как правило, не имеет представления о толп что делать в этом случае. Пользователь библиотеки может знать, как поступить в случае возникновения ошибок, но не в состоянии их обнаружить (если бы он мог — онн бы обрабатываллшь в коде пользователя, а их поиск не перепоручался бы библиотеке). В примере с калькулятором мы обошли эту проолему за счет того, что создали всю программу как сдпное целое. Благодаря этому мы смогли реализовать обработку ошибок, как часть общей системы. Однако, после того, как мы разделил~ логические части калькулятора на различные пространства пмсн, мы видим, что каждое пространство имен зависит от пространства Еггог (й 8.2.2) и что обработка ошибок в Еггог подразумевает, что каждый модуль ведет себя определсннылл образом после возникновения ошибки. Предположим, мы не можем позволить себе разрабатывать калькулятор как единое целое и пе хотим жесткой связи между Еггог и другими модулями.
Наоборот, пусть прн написании синтаксического анализатора (в частности) не было информации о том, как драйвер будет обрабатывать ошибки. Неслютря на простоту, в еггог(( реализована определенная стратегия обработки ошибок: пашеерасе Еггог', ш1по оу еггоге; с1ои61е еггог (сопегсЬа~ е( ( еЫ..сегг « 'ошибка. "«е « 'л,п'; по о1" еггогз-нл; ге1игп 1, Функция еггог () выводит сообщение об ошибке, возвращает некоторое значение. которос позволяет вызывающему модулю продолжить вычисления, и сохраняет информацию о состоянии, Су~цественным моментом является то, что каждая часть программы знает о существовании еггог (), о том как ее вызывать, н что от нее ожидать. Это слишком большие требования к программе, создаваемой пз независимо разработанных библиотек.
Исключения являются средствами С++ для отделения генерации информации о возшпсновсппи ошибки от ее обработки. В атом разделе кратко описаны исключения в контексте нх использования в примере с калькулятором. В главе 14 приводится более детальное обсуждение исключений и пх использования.
231 8.3. Исключения 8.3.1. 1)тгоьч и са1с)ч Понятие исключения (схсерйоп) введено для генерации в системе сообщения об ошибке. Например; вггис1йапуе еггог( т йапуе еггог(!л1!!)()=й,) О конструктор (З 252, у !023) сЬаг 1о сЬаг (!л1!) ( (! 1< литепс !!тйв<сЬагьстт ())) питег!с !!тйвк саагн:так ()к й) О ач. з 22.2 1Ьгот йапде егго г (!); ге1игл Ь Функция 1о сйаг () либо возвращает сЬаг по числовому значен!по г, либо генерирует исключение йалде еггог. Основная идея состоит в том, что функция, обнаружившая проблему, которую она не знает как решать, генерирует (1Ьгов) исключение в належлс, что вызывающий (прямо или косвенно) модуль знает, что делать в этой ситуации. Функция, которая собирается обрабатывать ошибку, может объянить, что она будет перехватывать (са1сЬ) исключения данного типа.
Например, для вызова 1о сйаг () и перехвата исключений, которые она может вызвать, мы могли бы написать: иоЫ у (!и! !) ( ггу ( сЬагс=1о сЬаг(!); 1,1- ) са1сй ',йапуе егго~) ( сегг « "проблема~и, ) Конструкция сагсЬ (1*- 1)( П- называется об!кабатчиком исключений. Она может использоваться только сразу ~!осле блока, начинаюшегося с ключевого слона 1гу, или сразу после другого обработчика; са1сЬ также является ключевым словом. В скобках находится объявление, которое попользуется аналогично объявлению аргументов функции. То есть, оно указывает тип объектов, которые могут быть перехвачены этим обработчиком, и (необязательно) присваивает имена перехватываемым объектам.
Например, если бы мы хотели узнать значение йалде еггог, мы могли бы задать имя аргумента са1сЬ точно также, как мы указываем имена аргументам функций. Например: Глава 8. Пространства имен н исключения ооЫй (Гпг(( ( ггу ( спасе = Го спас Я, 0- «асей (Капуе етгогх(( сегг« "проблема: Го спас('«хд «)'~п, Если код в (гу-блоке, или код, вызываемый из него, генерирует исключение, будут проверяться обработчики этого блока (гу.
Если сгенерированное исключение имеет тип, указанный в одном из обработчиков, будет выполнен этот обработчик. В противном случае обработчики игнорируются и (гу-блок ведет себя как обыкновенный блок. Если исключение сгенернрованно и ни один из 1гу-блоков не перехватил его, выполнение программы прекращается (Э 14.7). Обработка исключений в С++ в основном является методом п«редачн управления специальному колу в вызывающем модулс. Программисты на С могут рассматривать механизм обработки исключений как улучшенную замену яе()тр)1опу)тпр (э 18.1.2).
Важные связи между обработкой исключений и классами описываются в главе 14. 8.3.2. Выбор исключений Как правило, в каждой программе существует несколько возможных типов ошибок на этапе выполнения. Такие ошибки можно распределить между исключениями с различными именами. Я предпочитаю определять типы исключений только ради обработки исключений. Это сводит к минимуму путаницу, связанную с их назначением.
В частности, я никогда нс пользуюсь встроенными типами, такими как (пб для описания исключения. В большой программе нет эффективного способа разлеления исключений, обрабатывающих 1пб Я не мояу быть уверенным, что другие обработчики нс сменпцотся с моими, Наш калькулятор (э 6.1) должен обраба~ыва~ь две ошибки времени выполнения: синтаксические ошибки и попытку деления на ноль. Нет необходимости передавать какое-лиоо значение обработчику из кода, обнаружившего попытку деления на ноль, поэтому деление на ноль может быть представлено простым пустым типом, яГгисГХего 6Гои(е (); С другой стороны, обработчик скорее всего предпочел бы получать информацию о том, какого вида встретилась синтаксическая ошибка. В этом случае мы передаем строку: ястист пупсах еггог ( сопят сйаг* р; бупГах егтог(сопя1«лат'у((р" у;) Для удобства я добавил в структуру конструктор (э 2ЯЕ2, (( 10.2.3).
Пользователь синтаксического анализатора может разделить обработку этих двух исключений, добавив обработчики обоих исключений после блока 1гу. Прн генерации В.З. Исключения исключения выполнится соответствующий обработчик. По завершении обработки ис- клк>чения управление передается за конец списка обработчиков: 1гу ( //- ехрг (/а(ве); //мы попадел~ сюда в том и только в том случае, //если екрг() не возбудит исключения //- ) сагсп (Буп1ах еггог)( // обработка синтаксической о~падки васей (Хего й/аиде) ( О обработка деления на ноль //л~ы попадем сюди, если ехрг не сгенерировал исключения, либо если были // сгенернрованы исключения бупсак еггог или сего у!о~де // (и их обрабоспчики не сгенерировали исключения // сечи некоторым другим способом не изменили поспока управления).
Список обработчиков напоминает инструкцию вш(1с!с, с той лишь разницей, что не требуется инструкции Ьгеай. Синтаксис списка обработчиков отличается от списков сазе отчасти этим, а отчасти тем, что каждый обработчик является отдельной областью видимости (5 4.9А). Функции не требуется перехватывать все возможные исключения. Например, предыдущий блок 1гу не пытается перехватить исключения, которые потенциально могут возникнуть при выполнении операций ввода Эти исключения просто «передаются наверх > в поисках вызывающей функции с соответствующим обработчиком.
С точки зрения языка считается, что исключение обработано сразу после входа в его обработчик. Это сделано для того, чтобы любые исключения, сгенерированные во время выполнения обработчика, обрабатывались функцией с 1гд-блоком, вызвавшим исключение. Следующий пример не приведет к бесконечному циклу: с!авз/при1 ооег((ош ( /" ... "/ ), оои(,/() ( 1гу ( //- ) са1сй (!при1 ооег/(ош) ( 0- 1пгош 1при1 ооегЯочо (); Обработчики исключений могут быть вложенными. Например: с!а зз ХХ1т ( /" ... */ )' 234 Глава 8. Пространства имен и исключения Ь'...
!гу ( 0 са!сЕ ~ХХ!!) ( !гу ( 0 еложнмд код ! е (тхГ!И // о~иибха к сложном коде 0 ) Однако такая вложенность редко встречается в коде, написанном человеком, и, как правило, является признаком плохого стиля. 8.3.3. Исключения в программе калькулятора Имея базовый механизм обработки исключений, мы можем переделать пример с калькулятором нз 8 6.1, выделив обработку ошибок, возникающих на этапе выполнения из основноп логики калькулятора. Это прннедет к организации программы, которая бо.псе напоминает ту, что встречается в приложениях, сконструированных из отдельпых, слабо зависимых частей. Во-первых, можно исключить еггог().
Теперь функпия синтаксического анализатора будет знать только типы, используемые для сообпзения об ошибках: патекрасе Еггог( !л! по о)' еггогк; к!гас! Хего д!о!де ( Хего д!о!де() ( по оу еггогк++; ) ); к!гис! Ьуп!ах еггог ( соле! с!заг* р; Бул!ах еггог(соле! сааг' у) (р = ус по оГ еггогкз.+,) Синтаксический анализатор обнаруживает три синтаксические ошибки: Еехег оводеп иа!ие Еехегуе! !оаеп () ( икд1у патекрасе кЫ; УУ для доступа х тра 1, гка!р1т (! и нс д. (У 6.1.
7) деХаи!Е 0Е1АМЕ, МАМЕ= или онтоко (Г (гка!рда (сЕ)) ( к!г!пу оа!ие = сЕ, шЕг!е (!при! — )уе! (сЕ) &&!ка!пит (сЕ)) к!ппд оа!ие.рика Ьасу (сЕ); !при! — ь ри!Ьаса (сЕ); 235 8.3. Исключения ге1игп ситг 1оЬ=МАМЕ, 1»тот Еггоглдупсах еггог)" неверна» лексема'), ) доиб!е Рагяег.рот )Ьоо! уе1) б-. саяе 1.ехег:1.Р: доиЫе е = ехрг)1гие) ~~ )ситг 1оу ~=- Еехег.йР) 16гото Егготлдуп1 х еггог ) "ожидалась ')"); уе1 1одеп )); ге1итп е; О пропусптть скобки ') ' саяе Аехег;Е1чР: ге1игп 1; де1аипо ЬЬтоевЕттог..3уптах еггог) ожидалось первичное выражение"„' После обнаружения синтаксической овпюки используется 1пгов для передачи уп- равления обработчику, указанному в вызывающей (прямо плн косвенно) функции. Кроме то~ о, 1!!тою передает обработчику значение.