Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 52
Текст из файла (страница 52)
Яб.!. 7! 77 ... г(еуаий: 77 (чАМЕ, МАМЕ =, или ошибка (('(!за(рйа (сй) ) ( з(г!лд га(ие = сй( шй!(е(!при(->де((сй) ьа 1за(пит(сй) ) з(г!лд га(ие.ризй Ьасй (сй) ( (при!-ори(Ьасй (сй) ( ге(игп сигг (ой=МАМЕ; ) йгон Еггог::5уп(ах еггог (чЬад !ойеп" ); доиЫе Рагзег::рпт (Ьоо1 де!) 77 первичные выражения ( //,. сазе Еехег:: ЕР: ( ФоиЫе е = ехрг ((гие); (("(сиге (ой ! = Еехег::ВР) йгоп Еггог::Юул(ах еггог(" ' ) ' ехрес(еаш) ( де! (ойеп (); 77 пропустить )' ге!игп е( ) сазе Еехег:: ЕПР: ге(игп 1; деуаи((: йгон Еггог::Буп!ах еггог (чрптагу ехрес(еаш) ( При обнаружении ошибочной ситуации оператором (йго)г генерируется исключение с передачей управления на обработчки, определенный где-нибудь в вызывающем (прямо или косвенно) коде.
Оператор !Ьго(г передает обработчику также и значение. Например, следующий оператор йгоп 5уп(ах еггог (чрптагу ехрес(еат ) передает обработчику обьект типа .зуп(ах епог, содержащий указатель на строку "ргилагу ехрес!е((гз Вьивление деления на нуль не сопровождается передачей сопутствующих данных: 248 Глава 8. Пространства имен и исключения //умножение и деление йоив!е Рагеег:: гегт ( Ьоо! яе!) ( // ... сазе Аехег,: ВТ)г: (Т(йоиЫе й = рг!т ((гие) ) (еТ! /= й; Ьгеаат ) !лги»г Еггог:: Хего й!г!йе (); // ... ) Теперь можно писать управляющий код, который обрабатывает исключения Яулгах еггог и Хего й!гТйе: т! та(п (т! агес, слог* агвг ( ) ) ( /...
н Ы!е ( *(при!) ( тгу Аехег::ие! !олен () ( (Т(йехег:: сиге гоЬ == Еехег:: ЕМР) Ьгеаа; !1 (йехег:: сигг (оЬ == йехег:: РЯл/Т) сопйпие; сои!« Рагеег::ехрг(Та(ее) « ' тл'; ) сагсЬ (Еггог::Лего й/г!йе) ( сегг« "аиетр! го й!г!йе Ьу сего'тл"; й (Еехег::сиге гол ! = Еехег::РИ/»'Т) еа(р (); ) сота (Еггог:: Бунтах еггог е) ( сегг« "хунтах еггог:" «е.р « "тп"; (Т (Еехег:: сигг гол ! = Еехег:: Ри!ХТ) зЫР (); ) ) (Г(!при!! = ас!и) йе(е(е (при!! гелии по оТ еггог»м ) Во всех случаях, кроме ошибки в самом конце ограниченного лексемой РК)ХТ выражения (т.е. концом строки или точкой с запятой), функция та!л () вызывает «восстанавливающую» функцию з!с!р(). Эта функция пытается вернуть парсер в нормальное состояние, пропуская для этого символы до тех пор, пока не встретится конец строки или точка с запятой.
Очевидными кандидатами на членство в пространстве имен !ег!ггг являются !при! и х!с!р (); 249 8.3. Исключения патеврасе Р«1«ег ( лЫ:: 1легеат* три1; гоЫ вйр(); гоЫ Р«1«ег:: лЬ(р ( ) ( пЬИе (*1при1) сваг сЬ; триг- >яе1 ( сЬ ); У~пропускать символы, пока ке встретятся псы!1пе. или; лп 11сЬ (сЬ ) сале '~п': сале '; ': «егигп) ) ) ) 8.3.3.1. Альтернативные стратегии обработки ошибок Оригинальный код обработки ошибок был короче и элегантнее версии, использующей исключения. Однако это было достигнуто за счет более тесной связи между часгями программы.
Такой подход 1шохо масштабируется на большие программы, составленные из независимо разработанных библиотек. Можно рассмотреть вариант с отказом от отдельной функции обработки ошибок лЫр () за счет введения переменной состояния в функции «лат () . Например: ии та(п (1п1 агав, сяаг ага« [ ] ) И... Ьоо)т ее«о«=~а(ле; И пример плохого стиля и ЬИе ( *Р«1«ег:: три1 ) ( 1«у Код функции лйр() намеренно написан на более низком уровне абстракции, чем парсер, с целью избежать проблемы генерации новых исключений парсера во время обработки текуших исключений парсера. Возникает искушение слить воедино пространства имен Еггог и Х)«1 ег, ибо обработка ошибок и операции управляющей программы, такие как ввод/вывод, тесно связаны.
Например, подсчет числа ошибок и выдача сообшений о них вполне могли бы быть одной из задач управляющей программы. Однако лучше сохранять явное размежевание между сущностями, разными с логической точки зрения. Функцию гпат() я не стал помещать в пространство имен ХЬ1«ег. Глобальная функция гпа!п() является стартовой функцией программы 53.2); нет никакого смысла помешать ее в иное пространство имен. Будь наша программа существенно большего размера, заметная часть кода из функции «ланг() была бы перемешена в отдельную функцию из пространства имен Ю«1«ег.
Глава 8. Пространства имен и исключения ( Аехег::йе! !одел (); у Яехег:: сигг еоИ == Еехег:: ЕХР) Всеоб; «7 (Еехег:: сиге гоа == Еехег:: Рйтс(Т) (и егтог = уагеег сопипие; ) !Г((п епог ==уа(ее) сои!«Рапег::ехрг(Га)ее) « ' ~п'; ) сагсВ (Еггог:: Лего йгтйте) ( сегг««алетрг го 4)тЫе Ву геготп "; ш еггог = тгие; ) сагсВ (Еггог:: Буп~ах епог е) сегг« "туп!ах еггог:" « е.р« "',п"; ш еггог = тгие) ) ) у (Рптег::шри! )= ахи!::сш) йе(еге Рг(тег::шриг; гегигп Еггог:: по оГ еггоггп ) Я считаю это плохой идеей по нескольким причинам: 1.
Переменные состояния служат источником путаницы и ошибок, особенно если их действие распространяется на большие участки кода. В частности, я считаю версию функции та!и() с переменной состояния !л еггог менее читабельной, чем версия с функцией а!с!р () . 2. Стратегия раздельного сосуществования «нормального» кода и кода обработки ошибок в общем случае правильная. 3. Реализация кода обработки ошибок на том же уровне абстракции, что и код, порождающий ошибки, опасна; обрабатываюший ошибки код может снова породить те же самые ошибки, что вызвали текущую их обработку.
Я предлагаю в качестве упражнения вь(яснить, как это может произойти в версии функции таш () с !а егтог (88.5!7~). 4. Работы требуется больше при добавлении к «нормальному» коду кода обработки ошибок, чем при добавлении отдельных процедур обработки ошибок. Обработка исключений предназначена для решения нелокальных по своей природе проблем. Если ошибку можно обработать локально, так и следует поступать практически всегда. Например, нет ((икаких причин использовать исключения для обработки ошибки, связанной со слишком большим количеством аргументов командной строки: 251 8.4. Советы !и< та!и (!и! агдс, сйаг* акую ( ] ) из!пд патезрасе ззд! из!пе патезрасе Вг!юег! зп йсй (агйс) сазе 1: три! = ьс!п! Ьгеай ! сазе 1: !чтение командной строки !ириг= иеи !з!г!пдз!геат (агяю(1) ); Ьгеай; з(етаий: сегг« "!оо тану агдитеп!заик; ге!иги 1; о' чтение из стандартного потока ) ~У то же, что и раныие ) Далее исключения обсуждаются в главе 14.
8.4. Советы 1. Пользуйтесь пространствами имен для отражения логической структуры; 88.2. 2. Помешайте каждое нелокальное имя, кроме тайп () „в некоторое пространство имен; 88.2. 3. Проектируйте пространства имен таким образом, чтобы пользоваться ими было удобно и чтобы не было случайного доступа к пространствам имен, не имеющим отношения к вашей задаче; 88.2.4. 4. Избегайте слишком коротких названий для пространств имен; 88.2.7. 5. При необходимости применяйте псевдонимы для слишком длинных названий пространств имен; 88.2.7. б. Не нагружайте пользователей ваших пространств имен слишком сложными обозначениями; 88.2.2, 88.2.3.
7. Применяйте квалифицированные имена вида Фатезрасе:: тетЬег в определениях членов пространства имен; 88.2.8. 8. Используйте директиву из!пя патезрасе только в переходных целях или в локальной области видимости; 88.2.9. 9. Используйте исключения для разъединения кода обработки ошибок и нормального кода; $8.3.3. 10. Для исключений применяйте пользовательские, а не встроенные типы; 88.3.2. 11. Не пользуйтесь исключениями, когда достаточно локального контролирующего кода; 88.3.3.1. Глава 8. Пространства имен и исключения 8.5.
УПРажНЕНИЯ 1. ("2.5) Напишите модуль, реализуюший двусвязный список элементов типа зп(ля в стиле модуля огасл из 82.4. Проверьте его на списке названий языков программирования. Реализуйте функцию югг() для этого списка, и функцию, изменяюшую порядок элементов списка на обратный. 2. (*2) Возьмите не слишком большую программу, которая использует по край ней мере одну библиотеку, не применяющую пространств имен. Модифицируйте программу, чтобы в ней использовалось пространство имен для этой библиотеки. Подсказка: 88.2.9.
3. (*2) Реализуйте программу калькулятор в виде модуля в стиле 82.4, используя пространства имен. Не применяйте директивы «ял». Ведите учет допущенных вами ошибок. Предложите способ, как можно избежать таких ошибок. 4. (*1) Напишите программу, которая генерирует исключение в одной функ- ции, а перехватывает его в другой.
5. (*2) Напишите программу с функциями, вызываюшими друг друга с глуби- ной вложенности вызовов, равной 10. Снабдите каждую функцию аргументом, сообшаюшим уровень вложенности, на котором произошла генерация исключения. Заставьте функцию лга(в П перехватывать исключения и выводить информацию о перехваченном исключении. Не забудьте случай, когда исключение перехватывается в той же самой функции, где оно генерируется.