Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 94
Текст из файла (страница 94)
Однако сам конструктор может перехватывать такие исключения, помещая все тело функции — включая список ннициалцзаторов членов — в блок 1 у. Например: с1азв Х1 кесГаг и; Р- риб1гс Х аале~, ??- Х..Х ~Гп Г в) ггу . и 1«) О июгяиализаиия а при палгагяи в са1сЬ ~$есгвг:.Зиеев( г/здесь перехватываются исключения, г) сгенерированные при иниииализаиии и 14.4.6.2. Исключения и копирование Подобно другим конструкторам, копирующий конструктор может сигнализировать об отказе посредством исключения. В этом случае объект не создается.
Например, копирующему конструктору вектора часто необходимо выделять память и копировать элементы Я 16.3.4, з Д.З.2), и это может вызвать генерацию исключения. Перед генерацией исключения копирующему конструктору необходимо освободить все занятые пм ресурсы. Дев альное обсуждение обработки исключений и управления ресурсами для контейнеров приведено в з Д.2 и з Д.З. Естественно, сам обработчик ошибок может использовать стандартный набор основных способов выдачи сообшений об ошибке и восстановления. Каждый раз, когда исключение передается в вызывающую функцию, взгляд на то, что пошло неправильно, меняется.
Если с исключением передается подходящая информация, количество информации, имеюшей отношения к проблеме, может увеличиться. Другими словами, фундаментальной целью техники обработки исключений является передача информации об ошибке из точки ее обнаружения в место, где имеется достаточно информации для восстановления после возникновения проблемы и для осуществления этого надежным и удобным способом.
Техника «выделение ресурса есть инициализация» является самым безопасным и элегантным способом обработки ошибок в конструкторах, которые нуждаются более чем в одном ресурсе Я 14.4). По существу, эта техника сводит проблему работы с несколькими ресурсами к повторному применению (простой) техники работы с одним ресурсом. 14.5. Исключения, не являющиеся ошибками 427 Копирующее присвапвание похоже на копирующий конструктор тем, что оно может иметь захваченные ресурсы и может закончиться генерацией исключения.
1!еред генерацией исключения прсваиванне должно убедиться, что оба его операнда находятся в корректном состоянии. С другой стороны, могут быть нарушены требования стандартной библиотеки, что даст в результате непредсказуемое поведение (~ Д.2, 4 Д. 33), 14.4.7.
Исключения в деструкторах х -х(( ггу ( й, сагсгг ( (( ,г,г иеко|иорые действия (( ложе|и сгеиерироваигь исклюяеиие Функция стандартной библиотеки илсаиф( ехсер((огг () возвращает (гие, если было сгенерировано исключение, которое еще не перехвачено. Она позволяет программис- ту определять различные действия в деструкторе в зависимости от того, уничтожен объект нормально или в процессе раскручивания стека.
14.5. Исключения, не являющиеся ошибками Ес.ли исключение ожидается и перехвачено таким образом, что не оказывает отрицательного воздействия на поведение программы, почему это может считаться ошибкой? Только потому, что программист думает о такой ситуации как об ошибке, а о механизме обработки исключений — как о способе отреагировать на ошибку.
С другой стороны, можно рассматривать механизмы обработки исключений, как просто еше одну управляющую структуру. Например: ооггг Щиеие<Х>г> у( ( гу( Хог („(( С точки зрения обработки исключений деструктор может вызываться одним нз двух способов. [1] Пормальогыгг вызов: в результате нормального выхода имени из области видимости Я 10.4.3), использования оператора г(е1е(е Я 10.4.5) и т. д. (2) Вызов и процессе обработки исключения|в процессе раскручивания стека(Я 14.4) механизм обработки исключения приводит к выходу из области видимости, содержащей объект с деструктором, Во второл~ случае исключение не должно покинуть сам деструктор.
Если все-таки покинет, это считается ошибко(г механизма обработки исключений и вызывается з(г(г1еггпгпа1е (( Я 14.7). Все же не существует общего способа определить, в праве лн механизм ооработки исключений или деструктор проигнорировать одно нсклгочеиие ради обработки другого. Выход из деструктора через генерацию исключения также является нарушением требований стандартной библиотеки Я Д.2). Деструктор в состоянии защитить себя, если он вызывает функции, которые могут сгенерировать исключения.
11апример: Глава 14. Обработка исключений 428 // генерируеп~ Гтри1, // если очередь прел~а Х гп = у.уег (); //... си1сп (Т7иеие.Х>сЕтргу) ( ге1игл; ) ) со! с1/ай (Тгее' р, сопз1 и 1 сглупа е) ( (/ (е == р->ей) Ягою р; (/ (р->1еЩлс1 (1»1е/1, е); (/ (1»ггудг)~пс( (р->ггдЫ, е), //найдена Тгее'/)пс1 (Тгее" р, соле1згппуй е) ( 1гу ( /лс( (р, е); са1сй (Тгее* д) ( ге1игп д; //ч >51г= 5 ) гегигп 0; Однако неумеренное употребление исключений ведет к непонятному коду.
Всегда, когда это разумно, следует придерживаться точки зрения «обработка исключений является обработкой ошибок>. При таком подходе код оказывается понятным образом разделен падве категории: обыкновенный код и код обработки ошибок. Программа становится более осмысленной. К сожалению, реальный мир не разлагается на части также просто. Организация программ будет 1и до некоторой степени лолжна) отражать этот факт. Обработка ошибок традиционно сложна. Следует ценить все, что помогает реализовать понятную модель ошибки и предоставляет способ ее обработки. Такое решение в самом деле имеет цекоторос очарование, и это как раз тот случай, когда ие совсем понятно, что нужно считать ошибкой, а что — нет. Обработка исключений является менее структурированным механизмом, чем локальные управляющие структуры, такие как Ц'и /ог; к тому жс обработка исключении часто менее эффективна, если исключение действительно генерируется.
Поэтому исклкшепиями следует пользоваться в тех случаях, когда традиционные управляю~дне структуры являются неэлегаитным решением, или ими невозможно воспользоваться. Обратите внимание, что стандартная библиотека предлагает очередь из произвольных элементов с(иене без использования псклкзчений (э 17.3.2).
Исключения в качестве альтернативного механизма возврата могут оказаться элегантным методом завершения функций поиска, особенно глубоко рекурсивных, таких как поиск по дереву. Например; >4.6. Спецификации исключений 429 14.6. Спецификации исключений Генерация и перехват исключений изменяют способ взаимодействия между функци- ямн. Поэтому небесполезно указать (как часть объявления) набор исключений, ко- торые могут бьшь сгенерированы функцией.
Например: иоЫ/и ((пг а) Ягою (х2, хЗ), иоиЦ() Игом (х2, хЗ) ( 0 нечто эквивалентно: ооЫ Я ггу ( >>/ нечто саГс)> (х2) ( Г(>гав, ) саго(> (хЗ) ( г)>сои>, ) са(с)> ( )( вЫ: ипгхргсггг) ()> // повторная генерация О повторная генерация // ипгхргсгг>)() нг воввратигп управление Самым важным преимуществом является то, что обьлвлвние функции принадлежит интерфейсу, который видят те, кто ее вызывает. Определение функции, с другой стороны, не всегда доступно. Даже когда у нас есть доступ к исходному коду нсех наших библиотек, мы предпочитаем не заглядывать в него слишком часто.
Кроме того, функция со спеи>сфикацией исключений короче и проще, чем приведенная выше эквивалентная версия, написанная вручну>о. Предполагается, что функция, объявленная без спецификации исключений, может сгенерировать лк>бое исключение.
Например: тг/(); // л>ожет сгенерировать любое исключение Функции>, которая не генерирует исключений, можно объявить с пустым списком: (пгу() Йгои>(); // нг генерирует исключении Можно предлож>пь, чтобы умолчанием был запрет на генерацию исключений. Такое правило, однако, может потребовать спецификации исключений практически для каждой функции; оно станет причиной частых перекомпиляций и воспрепятствует взаимодейств>по с программами, написанными на других языках. Все это будет стп- Такое объявление означает, что/'() может сгенерировать только исключения х2, хЗ и искл>аления, являющиеся производными от этих типон, но нс другие.
Указывая таким способом, какие исключения она может сгенерировать, функция предоставляет пользователям действенные гарантии. Гели во время выполнения функция попытается нарушить взятые на себя обязательства, эта попытка будет трансформировала в вызов вЫ ипехрессес((). Смыслом ипехресгег/() по умолчанию является вызов вйс1егт(па1е (), которая в свою очередь вызывает абог~ (); подробности см. в 9 9.4.1.1. На самом деле Глава 14. Обработка исключений 430 мулировать программистов к «подрыву» механизмов обработки исключений и напи- санлило (не нужного нн для чего другого) подавляюшего нх кода.
А это, в свою оче- редь, создаст ложное чувство безопасности у людей, не заметивших подобной «под- рывной» деятельности. 14.6.1. Проверка спецификаций исключений На этапе компиляции невозможно обнаружить все случаи нарушения спецификации интерфейса. Тем не менее солидная проверка ца этапе компиляции все же осуществляется. О спецификациях исключений можно думать как о предположении, что функция сгенерирует все исключения, которые ей дозволены. Правила проверки спецификапий исключений во время компиляции легко выявляют нелепости. Если объявление функции содержит спецификацикл исключений, то и каждое объявление этой функции (включая определение) должно иметь спецификацию исключений с точно тем же набором типов исключений. Например: !агам Ятом (и!с(:.бас( а11ос); !пгр() ллллолаибка: атсуплствует специфика1!ая исключений ( л'л'...
с!аввВ( риЬИс, ои !и а! ооЫ Г(). Ыг!иа! иоЫд()!6тош(Х, У); о!глиа( воЫ 6 () 16голв (Х), )лг люлсет сгенерировапль любое исключение с1авв В риЫсс В ( вой () Ягою (Х), иоЫд () 16гош Д); ,лллпровильно /Л правильно: !)па() ил~еет оолее ограничительную л лл спецификацию, чел~ ВндН О овшбка: Г)лИ() ил~есгт менее ог)ланичительную Ллл спецификацию, челлВкц) иоЫ Л () !6гош (Х, У); Существенно, что не требуется проверять спецификацию исключений за пределами единипы компиляции. Естественно, компилятор мог бы это сделать, однако для большинства больших и долго живущих систем важно, чтобы этого не делалось — а если и делалось, то чтобы сообщения о серьезной ошибке возникали только в тех случаях, когда нарушения нельзя обнаружить на этапе выполнения. Смысл состоит в том, чтобы добавление исключения не приводило к необходимости полного обновления связанных спецификаций исключений н перекомпиляции всего потенциально зависящего от этого кода.