
Динамо-машины Метод Сократа
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [ 29 ] 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
Задача 13. Прагматичный взгляд
на спецификации исключений Сложность: 6
Сейчас, когда сообществом программистов на С++ накоплен определенный опыт работы со спецификациями исключений, пришло время систематизировать его. Наша задача посвящена вопросу применения спецификаций исключений с учетом особенностей различных реальных компиляторов.
Вопрос для новичка
1. Что произойдет при нарушении спецификации исключений? Почему? Каковы основные причины существования этой возможности С++?
2. Какие исключения могут быть сгенерированы каждой из перечисленных ниже функций.
int FuncC);
int GuncQ throwO ;
int HuncC) throw(A,B);
Вопрос для профессионала
3. Является ли спецификация исключений частью типа функции? Обоснуйте свой ответ.
4. Что собой представляют спецификации исключений и как они работают? Дайте точный ответ на поставленный вопрос.
5. Когда стоит использовать спецификацию исключений в функции? Почему вы используете (или не используете) эту возможность?
Решение
Как вы знаете, сейчас идет работа над новым стандартом С++ (рабочее название С++Ох). Давайте оглянемся назад и постараемся осмыслить накопленный опыт работы с текущим стандартом [С++03]. Подавляющее большинство стандартных возмож-ностей С++ просто замечательны, и именно им посвящена львиная доля публикаций, что НС удивительно - кому хочется твердить о недостатках! Слабые, мало используемые возможности языка чаше всего просто игнорируются и постепенно забываются (и это далеко не всегда плохо). Вот почему встречается очень мало статей о таких невразумительных возможностях языка, как vaiarray, bi tset и прочих - и в их число входят и спецификации исключений.
Давайте поближе познакомимся с имеющимся опытом использования стандартных спецификаций исключений С+ + .
Нарушение спецификации
1. Что произойдет при нарушении спецификации исключений? Почему? Каковы основные причины существования этой возможности С++?
Идея спецификаций исключений заключается в проверке времени выполнения того, что данная функция может генерировать только определенные типы исключений (либо не генерировать их вовсе). Например, приведенная ниже спецификация исключений гарантирует, что f может генерировать только исключения типа л или в *:
* Говоря точнее, если окружить эту функцию try/catch блоками для перехвата исключений А и В, то все возможные исключения будут перехвачены - в частности, такая функция может генерировать исключения, являющиеся производными классами от А и В. - Прим. ред.
int fC) throwС A, в );
Если будет сгенерировано исключение, которого нет в списке спецификации исключений, будет вызвана функция unexpectedC).
Пример 13-1
int f() throw(A,B) { А и В не связаны с С
throw с(); Будет вызвана функция unexpected
Вы можете з а р е г и стр и р о вать ваш собственный обработчик для этого случая при помоши стандартной функции set unexpected. Ваш обработчик не должен получать никаких параметров и не должен возвращать никаких значений.
void MyUnexpectedhandlerO { /* ... */ } std::set unexpected( AMyUnexpectedhandler );
Остается один вопрос - что может делать ваш обработчик? Единственное, чего он не может делать, - это выйти из функции при помощи оператора return. Поэтому у него есть два варианта действий:
преобразовать исключение в другое, допустимое спецификацией исключений, путем генерации исключения типа, имеющегося в списке спецификации исключений, вызвавшего вызов обработчика. Свертка стека при этом продолжится с того места, где она остановилась;
вызвать функцию terminate, которая завершает работу программы. (Функция terminate также может быть заменена другой, но в любом случае она должна завершить выполнение профаммы.)
Применение
Идея, лежащая в основе спецификаций исключений, очень проста: в программе С++ любая функция, если не указано иное, может генерировать исключения любого типа. Рассмотрим некоторую функцию Func.
2. Какие исключения мотуг быть сгенерированы каждой из перечисленных ниже функций. Пример 13-2(а)
int FuncО; Может генерировать любые исключения
По умолчанию в С++ функция Func может генерировать исключения любого типа, как сказано в комментарии к ней. Однако зачастую нам известно, что функция может генерировать только исключения определенных типов. В таком случае может оказаться разумным сообщить об этом компилятору и программисту. Например;
Пример 13-2(6)
int GuncQ throwO; не генерирует исключений
int HuncО throw(А,в); может генерировать только А или в
В приведенном примере спецификации исключений использованы для того, чтобы дать дополнительную информацию о функциях, а именно - о типах исключений, которые они могут генерировать. Комментарии, приведенные рядом с функциями, переводят спецификации исключений на обычный русский язык.
Первая мысль по этому поводу - чем больше информации, тем лучше, так что указание спецификации исключений функции - это всегда не плохо. Но это не обязательно так, поскольку зачастую в излишней детализации и кроется зло: хотя намерения у спецификаций исключений и благие, вымощенный ими путь может завести нас не совсем туда, куда хотелось бы.
Проблема первая - призраки типов
3. Является ли спецификация исключений частью типа функции? Обоснуйте свой ответ.
Джои Спайсер (John Spicer) из знаменитой Edison Design Group, автор большой части главы стандарта С++, посвященной шаблонам, назвал спецификации исключений С++ призрачными типами (shadow type). Одна из важнейших характеристик С++ - строгая типизация, и это хорошо и правильно. Но почему же мы называем спецификации исключений призрачными типами, а не частью системы типов С++?
Причина проста, хотя и имеет двойное дно :
спецификации исключений ие являются частью типов функций;
за исключением тех ситуаций, когда они являются частью типов.
Рассмотрим сначала пример, когда спецификации исключений не участвуют в образовании типа функции.
пример 13-3(а): спецификацию исключений нельзя
использовать в инструкции typedef.
void f() throw(A,в);
typedef void (*pf)() throw(A,B); ошибка
PF pf = f; невозможно из-за ошибки
Спецификация исключений не может использоваться в определении типа посредством typedef. С++ не позволит вам написать такой код, так что спецификации исключений не могут участвовать в типе функции... как минимум, в контексте typedef. Но в других случаях спецификации исключений в действительности участвуют в типах функций, если их записать без использования typedef.
Пример 13-3(6): все то же, но без typedef!
void fО throw(A,в);
void (*pf)() throw(A,B); ok
pf = f; ok
Кстати, такое присваивание указателя на функцию можно выполнять и в случае, когда спецификации исключений различны, но ограничения, накладываемые спецификацией исключений, при присваивании не ослабляются.
Пример 13-3(b): тоже вполне кошерно и с низким содержанием холестерина.. . :)
void fО throw(A,В);
void C*pf)() throw(A,B,c); ok
pf - f; ok, тип pf менее строгий
Спецификации исключений также участвуют в типах виртуальных функций, когда вы пытаетесь их перекрыть.
пример 13-З(г): спецификации исключений имеют значение
для виртуальных функций.
class С {
virtual void f() throw(A,в); некоторая спецификация
исключений
class d : С {
void fС); ошибка - спецификация
исключений менее строгая
Итак, первая проблема, связанная со спецификациями исключений, состоит в том, что в сегодняшнем С++ они являются призрачными типами , которые играют по правилам, отличным от обычных правил системы типов С++.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [ 29 ] 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |