Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 91
Текст из файла (страница 91)
Сейчас самое подходящее время для повторного прочтения 48.3, где изложены базовый синтаксис, семантика и приемы использования исключений. 14.1.1. Альтернативный взгляд на исключения Исключения — это такой термин, который разные люди понимают по-разному. Механизм исключений языка С++ разработан для обработки ошибок и других исключительных ситуаций (отсюда название механизма). В частности, он поддерживает обработку ошибок в программах, составленных из независимо разработанных программных компонентов. Механизм исключений предназначен для обработки лишь синхронных исключений, таких как выход за границы допустимых диапазонов в массивах или ошибки ввода/вывода.
Асинхронные события, такие как прерывания от клавиатуры и ряд арифметических ошибок, не обязательно являются исключениями и не обрабатываются непосредственно обсуждаемым механизмом. Асинхронные события требуют для своей обработки механизмов, принципиально отличающихся от механизма исключений (как мы его здесь определили). На многих системах предлагаются механизмы работы с асинхронными событиями, например сигналы, но поскольку эти механизмы системно-зависимые, то они здесь не рассматриваются. Механизм обработки исключений представляет собой нелокальную управляющую структуру, базирующуюся на раскрутке стека (згас)г ипи!пйп8) 6!4.4), которую можно рассматривать как альтернативный возврат из функции. Вполне законно использовать исключения, которые не имеют никакого отношения к обработке ошибок (б(4.5).
Но, конечно же, первичной целью этого механизма и предметом данной главы является именно обработка ошибок и обеспечение надежности программных систем. В стандарте С++ нет понятий потока и процесса. Следовательно, обстоятельства функционирования механизма исключений применительно к потокам и процессам здесь не рассматриваются. Средства параллельного выполнения, доступные на вашей системе, описываются в ее документации.
Здесь я просто отмечаю, что механизм исключений разработан таким образом, чтобы быть эффективным и в среде параллельного исполнения программ при соблюдении программистом таких правил, как блокирование разделяемых структур данных при доступе к эт(~м данным. ) 4.2. Группировка исключений 437 Механизм обработки исключений в С++ предназначен для того, чтобы обрабатывать ошибки и исключительные ситуации, и информировать о них. Задача программиста состоит в том, чтобы решить, какие ситуации являются исключительными для данной конкретной программы. Это не так легко сделать (В(4.5).
Можно ли считать исключительным событие, которое происходит почти что в каждом сеансе работы программы? Может ли запланированное и обрабатываемое событие считаться ошибкой? Ответ на оба вопроса — да. «Исключительное» не значит редко встречающееся или катастрофическое. Лучше рассматривать исключительные ситуации как «невозможность некоторой части системы выполнить то, что от нее требуется». Обычно в таких случаях следует предпринять что-то иное. Генерация исключений с помощью гйгом должна быть более редкой по сравнению с обычными вызовами функций, иначе структура программы станет неотчетливой. В то же время, мы можем твердо рассчитывать на то, что большинство грамотно спроектированных крупных программ наверняка генерируют и перехватывают некоторое количество исключений по ходу своего нормального и вполне успешного выполнения.
14.2. Группировка исключений Исключение является объектом некоторого класса, представляющим исключительную ситуацию. Обнаруживший ошибку код (часто это библиотека) генерирует исключение оператором йгом (В8.3). Часть кода, осуществляющая обработку исключений, сосредотачивается в блоке сагой. Оператор йгом порождает раскрутку стека до тех пор, пока не будет найден подходящий сагой-блок (в функции, прямо или косвенно вызвавшей функцию с этим оператором йгом). Часто исключения разбиваются на семейства.
Это означает, что наследование может отражать такое структурирование исключений и помогать в их обработке. Например, исключения в математической библиотеке могут быть организованы следующим образом: сглаз Майегг( ); сбззз О»его(оип риЬЕс Майегг( ); с!азз Юпг(евсин риигс Майегг ( ); с1азз ХегойчЫе: риигс Майегг ( ); гу... Это позволяет нам обрабатывать любое исключение как Майегг, не заботясь о точном его типе.
Например: гоЫ Г() ( ( ~У... ) сазсЬ (Огегугою) ( У обработка Оге~1ов или произеодкых от Огег)(ои Глава 14. Обработка исключений 438 са)сй ) Маейегг) 1 Ф обработка любых Ма)йегг кроме Ооег!)оы ) Здесь исключения типа 0«е«г?от обрабатываются специальным образом. А все остальные исключения из представленной иерархии подходят для обобщенной (одинаковой) обработки. Организация исключений в виде наследственной иерархии может быть полезна для обеспечения надежности кода.
Например, как бы вам пришлось обрабатывать исключения математической библиотеки без представленной выше иерархии? Ясно, что потребовалось бы занудное перечисление всех типов исключений: «оЫя() 1 гг« 1 о'... ) салай (0«ег))оы) 1 у* ... *у ) сагой (с)обегаю) 1 у* ... *! ) сагой (УегоЖ«)бе) ! у* ... *у ) ) И это не только утомительно. Программист вообще может при этом забыть какое-либо исключение. Далее, если мы добавим новое исключение в математическую библиотеку, каждый фрагмент клиентского кода, обрабатывающий все математические исключения, потребовал бы модификации.
Как правило, такие глобальные модификации недопустимы после выхода первой рабочей версии библиотеки. Часто они вообще невозможны, если не удается найти все необходимые куски кода. Рассмотренные проблемы сопровождения кода фактически ставят крест на попытках модификации системы исключений в библиотеках после их первоначального опубликования, что неприемлемо для большинства библиотек. Из рассмотренного вытекает, что в библиотеках или изолированных частях программных систем исключения должны организовываться в наследственные иерархии (в14.6.2).
Обратите внимание на то, что ни встроенные математические операции, ни базовые библиотечные математические функции (языков С/С++) не обрабатывают арифметические ошибки как исключения. Одной из причин такого явления служит факт, что арифметические ошибки вроде деления на нуль проявляются асинхронно на многих машинах с конвейерной архитектурой. Приведенная выше иерархия исключений Магйе««является просто иллюстрацией. Стандартные библиотечные исключения языка С++ описываются в 5!4.10.
14.2.1. Производные исключения Использование иерархий классов для обработки исключений естественным образом приводит к обработчикам, интересующимся лишь подмножеством информации, которую несут с собой исключения. Другими словами, в типичном случае ис- 14.2 Группировка исключений ключение перехватывается обработчиком его базового класса, а не обработчиком его собственного класса. Семантика именования и перехвата исключений аналогична таковой для функций с аргументом. То есть формальный аргумент инициализируется фактическим значением вызова 57.2). Это означает, что сгенерированное исключение «срезается» (з!)сес() до перехваченного (512.2.3).
Например: с!ам Ма!лег ( р .. ипил! юЫ йеаия рппг() сопв! ( сезт « "Ма!Ь еггог"; 1 с!ам 1л! оюг1(опп риЫЫ Мазлегг ( солж сааг* ар ! !л!Ы, аг; риис: 1п! очегг)озч (солж сааг* р, т! а, злг М ( ор = рз а! = а; а2 = Ь; ) ч(пиа1 чоЫ аваля рппг() сопв! ( сезт « ор « ' ( ' «а1 « ', ' «а2 « ' ) '; ) р... )' чо!а ! () ( иу ( 0() ' ) са!сЬ (Ма!Ьезт т ) ( У ... ) ) Когда вызывается обработчик для Ма!Ьегг, т является объектом типа Ма!Ьегг— даже если функция я() во время своей работы сгенерировала исключение типа 1п! очег12оп. Это означает, что добавочная информация, сопутствующая типу 1л! очег11оп, недоступна.
Как всегда, можно использовать указатели или ссылки во избежание потери информации. Например: т! ааа'(1лгх, тгу) ( »1( (х>0 ««у>0 ««х>11«Т МАХ-у) ) 1 (х<0 ай у<0 ьа х<17УТ МТУ-у) ) !Ьгозч!пг очезугоп("»",х,у); ге!игп х»уз Ю вычисление х+у не вызовет исключений ) юЫ1() ( иу Глава 14: Обработка исключений 440 (иг (1 = аи(1,2); (а( 12 = аг(г((1(«(Т МАХ, -2); (и( (3 = ай~(1(«(Т МАХ, 2); У приехали! ) са(ся (Ма(веггь т) ( т.деЬия рг(а((); ) ) В результате последнего из указанных вызовов а(Ы() генерируется исключение, приводящее к вызову метода 1аг огег((ои:: ИеЬад уг(а(О.
Если бы исключение перехватывалось «по значению», а не «по ссылке», то был бы вызван метод Ма(- Ьегг::г(еЬая рг(а(() . 14.2.2. Композитные (комбинированные) исключения Не каждая группа исключений обязана образовывать древовидную структуру. Бывает, что исключение одновременно принадлежит сразу двум группам. Например: с1ат Ь(еЯе егг: риЫ(с ()(еогога еп, риЫЬс р((е луе(ет егг ( (* ...
*l ) ( Исключения Мер(1е егг могут перехватываться как функциями, настроенными на работу с сетевыми исключениями го(~Ц'() ( (гу ( У что-либо ) сагсЬ (л(еагога еп ь е) ( о'... ) ) так и функциями, настроенными на работу с файловыми исключениями: »оЫа() ( (гу ( У... ) са(ся (Ьт(е лугает еггь е) ( У... ) ) Такие комбинированные (композитные) исключения важны в тех случаях, когда, например, сетевые службы прозрачны для пользователей — автор функции л() 441 14.3. Перехват исключений в нашем примере мог не догадываться о том, что сетевые службы вовлечены в дело (см. также 514.6). 14.3. Перехват исключений Рассмотрим пример: аоЫ2'( ) ( ( гаков Е(); ) гаага (Н) ( ?? когда мы сюда аоаадем? ) ) Обработчик исключений будет вызван в следующих случаях: 1. Если Н того же типа, что и Е.
2. Если Н вЂ” это однозначный базовый класс для Е. 3. Если Ни Š— указательные типы, и [1[ или [2] справедливы для типов, на которые указывают эти указатели. 4. Если Н вЂ” это ссылка, и [1[ или [2[ справедливы для типа, на который Нссылается. Мы также можем добавить модификатор сопзг к типу исключения, указанному в блоке сагсй (точно так же, как это делается в отношении аргументов функций).