Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 91
Текст из файла (страница 91)
Исключения стандартной библиотеки описаны в ф 14.10. 14.2.1. Производные исключения Использование иерархий классов для обработки исключений естественным образом приводит к обработчикам, интересующимся лишь подмножеством информации, которую несут с собой исключения. Другими словами, исключение обычно перехватывается обработчиком его базового класса, а не обработчиком его собственного класса. Семантика перехвата и задания имен исключений идентична семантике функции с аргументом. То есть формальный аргумент инициализируется значением аргумента (б 7.2). Из зтого следует, что сгенерированное исключение чсрсзается» до перехваченного (2 12.2.3). Например: с!аее Ма!Ьегг( 77-.
о!гГиа! ооМ с)еЬид рнп! () соле! сегг « "Математическая ошибка'; ) ); с!аее Ра! ооегЯото; риЫгсМагдегг( сопи сйаг* ор !ага!, а2; риЫ!с; !и! ооег)!от(соле!сдаю р, тга,!п1Ь)(ар=р;а! =а; а2=Ь;) о!гела! ооЫ с(ебиа рг7п! () соле! ( сест «ор « ' (' «а! « ',' «а2 « ')'; ) оо!с(!'() ( 413 14.2. Группировка исключений ггу ( ХО; сазсЬ (МаГЬеггт) ( о.. Когда вызывается обработчик Ма1Ьегг, т является объектом Ма1Ьегг — даже если вызов д () привел к генерации 1п1 оиег/1ощ. Это означает, что дополнительная информация, имеющаяся в 1п1 ооегЯою, недоступна. Как всегда, можно использовать указатели или ссылки во избежание потери информации.
Мы могли бы, например, написать; шг ойй (1пз х, шг у) (/((х>О ЕЕ у>ОЫ х>ИТ МАХ вЂ” у) ( (х<018 у<Оййх<1ЫТ МП-у)) ГЬгосо!пГ ооег9осо( +", х, у); гезигп х+у; // в результате х+у не произойдет переполнение оо(йЩ ( (гу ( (ос (1 = айй (1, 2); (пГ (2 = айй (Тк'Т МАХ, -2); ш1И=айй(11УТ МАХ,2); // Приехали! саЗсЬ (Майеггй т) ( //-. т.йебиу ргш~ (); ) Последний вызов асЫ () приведет к исключению, которое вызовет 1п1 опегЯопг,йебид рг/п1(). Если бы исключение перехватывалось по значению, а не по ссылке, была бы вызвана функция Ма1ЬеггсйеЬид ргзп1(). 14.2.2.
Композиция иоключений Нс каждая группа исключений является древообразной структурой. Довольно часто исключение принадлежит сразу двум группам. Например: // ошибка, связанния с файлом в сети с1азз1зеуйе егг риЫ(с1зейоогЬ егг риЬИсрйе зуз1ет егг(/'., */); Жесй(е егг может перехватываться функциями, работающими с исключениями в сети: сои /() ( оу( // что-нибудь 414 Глава 14, Обработка исключений сассп ()ЧеРыогя еетй е) ( и ) ) а также функциями, работаюгцими с исключениями файловой системы: ооЫу () ( геу ( 0- ) сассЬ. 'Ггее еуесеас ееей е) ( /с".
) ) Такая неиерархическая организация обработки ошибок имеет большое значение в тех случаях, когда службы (например, сетевые) прозрачны для пользователя. В нашем примере автор у () мог и не подозревать о сугцествовании сети (см, также 5 14.6). 14.3. Перехват исключений Рассмотрим пример: опии ( ьгу ( Гйгош Е (); ) сагой (Н)( Ц коеда ма~ сюда попадем? Обработчик будет вызван: [1] Если Н того же типа, что н Е. [2) Если Нявляется однозначной открытой базой Е. [3) Гели Ни Е являются указателями, и [1) или [2) выполняется для типов, на которые они ссылаются. [4( Если Н является ссылкой, и [1) или [2) выполняется для типа, на который Н ссылается.
Кроме того, мы можем добавить модификатор соле(к типу, используемому для перехвата исключения, точно так же, как мы можем добавить его к аргументу функции. Это не изменит набор исключений, который мы сможем перехватить, а только воспрепятствует модификации перехваченного исключения. В принципе, исключение в момент его генерации копируется, поэтому обработчик имеет коп1по исходного исключения. В действительности, исключение может сканироваться несколько раз до того, как оно будет перехвачено. Следовательно, мы не можем сгенерировать исключение, которое не может быть скопировано.
Реализа- 415 14.3. Перехват всех исключений циям позволительно применять широкий набор стратегий хранения и переда ги исключений. Однако гарантируется, что оператору пети всегда хватит памяти для генерации стандартного исключения нехватки памяти бас( аПос (5 (4.4.5). 14.3.1. Повторная генерация Перехватив исключение, обработчик часто решает, что он не может полностью обработать ошибку. В этом случае обработчик делает то, что может, после чего вновь генерирует исключение. Таким образом, ошибка может быть обработана в наиболее подходящем месте. Это происходит даже в том случае, когда информация, требуемая для корректной обработки ошибки, не сосредоточена в каком-то одном месте, так что действия по восстановлению лучше распределить по нескольким обработчикам.
Например: оо(дй() ( 1гу ( // код, который может привести к мигпелгата«еским ошибкам са1сп (й4аЖегг) ( (( (сап Бапд(е И сотр1е1е!у) ( // в состоянии полностью обработать огиибку? // обрабогпка гг(и1(тегг ге1игп, е(ве ( //делается то, чгпо можно сделать здесь усев, // повторная генераипя исклю«ения Факт повторной генерапии отмечается отсутствием операнда у(йгош.
Если осуществлена попытка повторной генерации при отсутствии исюпочення, будет вызвана функция 1егш(па1е () Я 14.7). Компилятор может обнаружить некоторые таюие случаи, но не все. Повторно генерируемое исключение является исходным исключением, а не просто его частью, которая была доступна как Ма(йегг. Другими словами, если было сгенерировано 7п1 ооегу(ош, то функция, вызвавшая Ь (), по-прежнему может перехватить 7п1 овегЯош, которое было перехвачено в й() в виде Ма(йегг и повторно сгенерировано. 14.3.2.
Перехват всех исключений Описываемая ниже вырожденная форма техники са(сй-1гсгов может иметь большое значение. Также как и в функциях, многоточие ... означает «любой аргумент» (ч 7.6), поэтому са1сй ( ..) означает «перехват всех исключений». Например: ио1с( т () 1гу ( Глава 14. Обработка исключений 416 // что-нибудь ) са!с/г (...) ( //обработка всех исключений // очистка !!гевал То есть если исключение возникает в результате выполнения главной части т (), вызывается часть обработчика, осуществляющая очистку. После того, как локальная очистка завершена, исключение, приведшее к ней, повторно генерируется для дальнейшей обработки ошибки.
См. в 5 14.6.3,2 описание метода получения информации об исключения, перехваченной обработчиком с .... Одним важным аспектом обработки ошибок вообще и обработки исключений в частности является поддержка инвариантов, предполагаемых программой Я 24.3.7.1). Например, если предполагается, что функция и () оставляет определенные указатели в том состоянии, в котором она их нашла, то мы можем написать в обработчике код, задагогций им приемлемые значения.
Таким образом, обработчик перехватывающий все исключения, можно использовать для поддержки произвольных инвариантов. Однако во многих важных случаях такой обработчик является не самым злегантным решением данной проблемы (см. з 14 4). 14.3.2.1. Порядок записи обработчиков Ввиду того, что производные исключения могут быть перехвачены обработчиками, предназначенными для более чем одного вида исключений, порядок, в котором записаны обработчики в инструкции !гу, имеет большое значение. Эти обработчики проверяются по порядку.
Например: ос!се) ( !гу ( //. ) са!с!! (я!с!"1оя Ьаяе:уа!!иге) ( О ооработка любой ошибки в потоке вввода/вивода (у ! 4. 10) ) са!с!г (я!дгехсер!1оай е) ( //обрабоглкалюбого исключения стандартной библиотеки Гу" !4. !0) ) са!с/г ( ..)( Ообраооткалюбого другого исключения Ц !4.3.2) ) Так как компилятор знает иерархию классов, он можст обнаружить многие логичес- кие ошибки. Например: ооЫд() ( ггу ( 417 14.4. Управление ресурсами са1сЬ (...) ( О обработка любого другого исключения (у 14.3.2) са1сб (зЫсехсерг!опй е) ( Ообраооткаяюбогоисключения стандартной оиолиотеки (у !4./О) ) са1сЬ (з(д,бас! сазй( 0 обработка сбоя дупагп!с саз! (у 15.4.2) В этом примере вариант с ехсервоп никогда не будет рассматриваться.
Даже если бы мы убрали обработчик, «перехватгявающий все», бас( саз1 никогда бы нс рассматривался, потому что он является производнгам от ехсер11оп. 14.4. Управление ресурсами В тех случаях, когда функции требуется некоторый ресурс (например, функция открывает файл, выделяет свободную память, блокирует доступ к ресурсу), для дальнейшего функционирования системы очень важно, чтобы ресурс был правильно освобожден. Довольно часто зто «правильное освобождение» ресурса осуществляется той же самой функцией, которая его затребовала, перед возвратом. Например: иоЫ изе )!1е (сопя!сдаю Я Р$3Е',1= 1ореп $п, "ю"); гг) использование1 1с)озе ()); Это выглядит приемлемо до тех пор, пока вы не поймете, что ес.ли что-нпбудь произойдет после вызова)ореп () и до вызова ус1озе (), исключение может привести к выходу нз изе )сз1е () без вызова)сс1озе (). Точно такая же проблема существует и в языках, не поддерживающих обработку исключений.
Например, функция стандартной библиотеки С 1опд1шр () может привести к подобной ситуации, Даже обыкновенная инструкция ге1игп может завершить изе )з1е без закрытия 1'. 11ервая попытка сделать функцию изе 111е () устойчивой к ошибкам выглядит следующим образом: ооЫ иве Я1е (сопя! сдаг"1'и) ( сП Е*1'= 1ореп ()и, 'г"), гсу ( // исиользование1 саге)з ( .)( 1с1озе ()); аспас 418 Глава 14.