Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 51
Текст из файла (страница 51)
Теперь представим, что у нас нет возможности создавать калькулятор в виде единого целого, и что мы хотим избежать тесной связи между Еггог и другими модулями. Вместо всего этого предположим, что синтаксический анализатор (парсер) и другие модули разрабатываются в отсутствии информации о том, как управляюшая программа будет обрабатывать ошибки. Несмотря на всю простоту функции еггог(), определенная стратегия обработки ошибок в нее была заложена: патеерасе Еггог ( гпг по о(' еггогю доиЫе еггог (сопл сааг* е) еиг::сеп « "еггог: "«з « ' Хо'; по о(' еггогечч; гегигп 1; ) Функция еггог() выводит сообшение об ошибке, возврашает некоторое предопределенное значение, позволяющее вызвавшему ее коду продолжить работу, а также регистрирует ошибку в переменной по оу еггогж При этом очень важно, что все части программы знают о существовании функции еггог (), знают как ее вызывать, и что ожидать от этой функции.
К программам, составленным из независимо разработанных бибилиотек, невозможно предъявлять столь большие требования. Исключения яшш ются средством языка С++, позволяюшим отделить информирование об ошибках от их обработки. В данном разделе исключения описаны очень кратко и только в контексте их использования в программе калькулятора.
Более обстоятельное изучение исключений и случаев их применения сосредоточено в главе 14. 8.3.1. Ключевые слова 1)зго)(и и са1с)т Концепция исключений (ехсербопз) введена в язык для генерации сообшений об ошибках. Например: 243 8.3. Исключения виисг Капле еггог ( 1пг (г Лапке еггог(1пг й) (1=й; ) ): У конструктор (32.5.2, ~10.2.3) слог го сваг(1пг 1) ( (1(1<питег(с 11т1(в<сйаг>:: т(п ( ) ( ) питег(с 11т((в<сйаг >:: так () <1) (гсм 3222 гйгот Канве еггог (); гесигп сг ) Функция го с))аг() либо осуществляет нормальный возврат значения переменной (типа сйаг, либо генерирует исключение Яаще еггог.
Фундаментальная идея заключается в том, что если функция обнаруживает ошибочную ситуацию, с которой не в состоянии справиться сама, то она генерирует исключение ((нппо ехсербоп) некоторого типа в надежде на то, что вызывающая функция проблему так или иначе уладит. Функция, способная решить такую проблему, явным образом указывает намерение перехватывать исключение (са(сн ехсер1юп) указанного типа. Например, для вызова функции го сйаг() и перехвата генерируемых ею исключений, нужно написать следующий код: гоЫ а ( 1нг 1) ( сйаг с = го айаг (1); о' ... ) со(си (Капле еггог) ( сегг« "оорв~п"; ) Конструкция сагсн( l* ...
*! ) ( ж... ) называется обработчиком исключений (ехсергюп пап(11ег). Эта конструкция может располагаться только сразу после блока, помеченного ключевым словом пу, или сразу после другого обработчика исключений; сагой является ключевым словом языка С++. В круглых скобках располагается объявление того же типа, что и объявления аргументов функций. То есть оно специфицирует тип объектов, которые могут быть перехвачены обработчиком, а также может именовать этот объект (опционально).
Например, если бы мы захотели узнать значение сгенерированного в качестве исключения объекта типа Хапае еггог, мы бы снабдили этот объект именем так же, как мы снабжаем именами аргументы функций. Например: 2ЯЯ Глава 8. Пространства имен и исключения гоЫ Ь (1и11) ( о'у ( соаг с = то соаг(1); // ... сагса ( )Гаияе егго х ) ( сегг« "ооря: го сЬаг(" «х.1 « ") ~и"; ) ) Если код в ггу-блоке (тгу-о(ос/с), или код, вызываемый из него, генерирует исключение, будут проверяться все обработчики, относящиеся к данному ггу-блоку.
Если тип сгенерированного исключения совпадает с объявленным в одном из обработчиков, то выполняется код этого обработчика. Если же ни один из обработчиков не подошел по этому критерию, то все обработчики игнорируются, и в этом случае ггу-блок ведет себя так же, как и обычный блок кода. Если сгенерированное исключение не перехватывается обработчиками, то программа принудительно закрывается (514.7). В своей основе, обработка исключений сводится к передаче управления из вызывающей функции в специально указанный (назначенный) код.
Если требуется, то дополнительная информация об ошибке может быть передана в вызывающую функцию. Программисты на С могут рассматривать механизм обработки исключений как улучшенную замену для яе(пир/1опдтр (516.1.2). Важная тема .взаимоотношений обработки исключений и классов рассматривается в главе 14. 8.3.2.
Обработка нескольких исключений В общем случае в программе могут возникать разные ошибки иа этапе выполнения (гил-гиле еггогэ). Этим ошибкам можно сопоставить несколько типов исключений с различающимися именами. Я предпочитаю для обработки исключений использовать специально предназначенные для этого типы (чтобы предельно ясно выразить через них свои намерения). В частности, с этой целью я никогда не использую встроенные типы (вроде 1яг). В большой программе не будет эффективных способов удостовериться, что такие обработчики имеют дело исключительно с предназначенными для них ошибками.
То есть возможна путаница в обработке ошибок из разных источников. Наша программа калькулятор (56.1) должна обрабатывать два типа ошибок: синтаксические ошибки и попытки деления на нуль. Нет необходимости передавать обработчику ошибки деления на нуль какую-либо дополнительную информацию, так что этот тип ошибок может быть представлен следующим простейшим (пустым) типом: эсгис( Уего «1гЫе ( С другой стороны, обработчик синтаксических ошибок наверняка захочет узнать, каков характер ошибки. В этом случае мы передаем строку: 8.3.
Исключения зггис1 Бунтах еггог ( сопзгсйаг* р; яупзах еггог(солт с)ьаг* 4) (р = 4; ) )' Для удобства я добавил здесь в структуру конструктор (52.5.2, 5) 0.2.3). Пользователь синтаксического анализатора может осуществить обработку этих двух исключений, добавив обработчики обоих типов к (гу-блоку. По ситуации будет выполняться один из них. По выходе из тела обработчика управление передается коду, следующему за самым последним обработчиком в списке: пу ( /'...
ехрг (/а(зе) 1 // сюда попадаем только если ехрг() не возбуждает исключение // ... ) саге/ь(Лунгах еггог) ( /У обработка синтаксической ошибки ) сагой (лего Ж»Ые) У делаем что-то в ответ на попытку деления на нуль ) // сюда попадаем, если ехр не вызвала исключения или если бьии исквючения // Купгох еггог или Хего д/хше, а их обработчики не измени«и потока управления //с помощью ге/игл, йгош или каким-либо иным способом. Список обработчиков выглядит почти как оператор в)в)гсй, только не требуется оператор Ьгеак. Это отличие в синтаксисе призвано подчеркнуть тот факт, что каждый обработчик образует отдельную область видимости (54.9.4).
Функции не обязаны перехватывать все возможные типы исключений. Например, предыдущий 1гу-блок не имеет обработчика потенциально возможных исключений от операций ввода. Эти исключения просто оставляются для дальнейшего поиска обработчиков (передаются «наверх» по стеку вызовов функций). С точки зрения синтаксиса языка исключение считается обработанным сразу же после входа в обработчик. Это сделано для того, чтобы исключения, сгенерированные в теле этого обработчика относились к «вышестоящему» коду, вызвавшему соответствующий (гу-блок. Например, в следующем коде нет бесконечного цикла обработки исключений: с(азз!приз оиегу)ои ( /* ...
*/ ); иоЫ/'() ( ггу 246 Глава 8. Пространства имен и исключения салоп (1приг огег/(от) ( // ... гй от!прог огегЯот(); Обработчики исключений могут быть вложенными. Например: с1аллХХИ ( /* ... */ ); гоЫ~() ( // ... пу // ... са1с/ь (ХХИ) ( ггу // что-нибудь нетривиальное ) саге)ь (ХХИ) ( // код нетривиального обработчика са и "грохнулся ь ) // ...
) Однако такая вложенность редко встречается в коде, написанном человеком, и вообше-то является плохим стилем. 8.3.3. Исключения в программе калькулятора Отталкиваясь от базового механизма обработки исключений, мы можем переработать код калькулятора из 56.1 таким образом, чтобы отделить обработку ошибок времени выполнения от основной логики программы. Это приведет к такой организации кода, которая хорошо отражает структуру реальных программ, построенных из отдельных, слабо зависимых частей.
Сначала устраним функцию еггог () . Вместо этого функции парсера будут знать лишь типы, сигнализируюшие об ошибках: патеврасе Еггог ( 1пг по о~' еггот," воисг Уего ЖьЫе ( Хего Йыие( ) ( по о/ еггогль+; ) 8.3. Исключения жгис! Яуп(ах еггог ( солт сйаг* р; Яуп!ах еггог(солз! сйаг* а) ( р = ч( по оу еггогзее( ) ) ) Парсер (синтаксический анализатор) генерирует три типа ошибок: Еехег:: Тойел га!ие Еехег::Ле! (ойеп () из(лл латезрасе зЫ; 77 для доступа к !при(, Иа(рйаО и т.д.