
Штерн В. - Основы C++. Методы программной инженерии - 2003
.pdf800 |
Часть IV # Расширенное исоользование С-^+ |
||||
Enter numerator and positive |
|
В этой задаче нулевое значение знаменателя недо |
|||
|
пустимо и в сообндении отклоняется. Отрицательное |
||||
denominatior |
(any letter to quit): 21 42 |
значение делителя также не принимается. Если дробь |
|||
Value of the fraction: |
0.5 |
|
|||
Enter numerator and positive |
|
отрицательная, необходимо сделать отрицательным |
|||
21 0 |
числитель. Отрицательное значение делителя должно |
||||
denominator |
(any letter to quit): |
отклоняться с сообидением, в котором также выводит |
|||
Zero denominator is not allowed |
|
||||
|
ся недопустимое значение. |
||||
Enter numerator and positive |
42 -70 |
Входной цикл выполняется до тех пор, пока пользо |
|||
ватель не введет букву вместо числовых входных дан |
|||||
denominator |
(any letter to quit): |
||||
Negative denominator: -70 |
|
|
ных. Оператор cout возвращает нуль, а оператор break |
||
|
|
прерывает цикл. Пример вывода для программы пока |
|||
Enter numerator and positive |
|
зан на рис. 18.1. |
|||
denominator |
(any letter to quit): 42 70 |
В этом примере оба исключительных состояния |
|||
Value of the fraction: |
0.6 |
|
(нулевой знаменатель и отрицательный знаменатель) |
||
Enter numerator and positive |
|
обнаруживаются в клиентской программе, а ошибки |
|||
denominator |
(any letter to quit): exit |
обрабатываются сразу же /ю месту их обнаружения. |
|||
|
|
|
|
Серверные функции inverseO и fraction() не могут |
|
Рис. 18.1. |
Вывод для |
программы |
столкнуться с ошибочными входными данными. Имен |
||
|
из листинга |
18.1 |
|
но поэтому они выполняют вычисление результатов |
|
|
|
|
|
для вывода безоговорочно, без тестирования допусти |
|
|
|
|
|
мости входных данных. |
|
|
Устранение ошибки происходит здесь с помош,ью вывода сообш,ения об ошибке |
||||
|
и повторения запроса следуюш,их входных данных. Основная программа (вызов |
||||
|
серверной функции fractionO) не отделяется от программы обработки ошибки, |
||||
|
но это не вызывает в результате серьезных проблем. |
||||
|
Часто случается так, что ошибку можно обнаружить только после выполнения |
||||
|
некоторой обработки в серверной программе, далеко от того места, где появилась |
||||
|
ошибка. Некоторые из таких ошибок могут обрабатываться по месту их обнару |
||||
|
жения. Однако для других ошибок может потребоваться дополнительная инфор |
||||
|
мация, отсутствуюш,ая |
в серверной функции, где и была обнаружена ошибка. |
В этом случае информация об ошибке должна возвращаться в клиентскую про грамму для обработки и, если возможно, для исправления. Смоделируем такую ситуацию. Передадим проверку входных данных из клиентской программы в сер верную функцию InverseO.
В листинге 18.2 показан этот подход к обработке ошибок. Функция inverseO вычисляет обратное значение своего аргумента. Если значение параметра равно нулю, inverseO использует константу DBL_MAX, определенную в заголовочном файле cfloat или float, h, как обратное значение. Затем она проверяет ответ, определяет допустимость результата и сообщает вызывающей программе, что произошло во время вывода.
Листинг 18.2. Пример программы с обнаружением ошибки в серверной программе
#inclucle |
<iostream> |
- |
|
|||
#include |
<cfloat> |
|
||||
using |
namespace |
std; |
|
|
||
inline long |
inverse(long |
value, double& answer) |
||||
{ answer = (value) ? 1.0/value : DBL_MAX; |
|
|||||
if |
(answer==DBL_MAX) |
|
allowed\n\n"; |
|||
|
{ cout « |
"\nZero denominator is not |
||||
|
return 0; } |
|
// нулевой знаменатель |
|||
else if(value < 0) |
|
// отрицательный знаменатель |
||||
else |
{ |
return value; } |
||||
|
1; } |
|
// допустимый знаменатель |
|||
|
return |
|
Глава 18 • Программирование с обработкой исключительных ситуаций |
801 |
|||||||||
inline long |
fraction |
(long пДопд cl,clouble& |
result, char* &msg) |
|
|
|||||
{ long |
ret |
= inverse |
(d, result); |
/ / |
result = 1.0 / d |
|
||||
i f |
(ret -= |
1) |
|
|
/ / |
допустимый |
знаменатель |
|
||
|
{ result |
= n * |
result; } |
/ / |
result = n / |
d |
|
|||
i f |
(ret < 0) |
|
|
|
|
|
|
|||
|
msg = "\nNegative denominator: "; |
|
|
|
|
|||||
return ret; |
} |
|
|
|
|
|
|
|||
int |
mainO |
|
|
|
|
|
|
|
|
|
while |
(true) |
|
|
/ / |
числитель/знаменатель |
|
||||
{ long numer, denom; double ans; |
|
|||||||||
|
char |
*msg; long |
ret; |
/ / |
информация об ошибке |
|
||||
|
cout |
« |
"Enter |
numerator and positive\n" |
|
|
|
|
|
« |
"denominator |
(any letter |
to |
quit) |
/ / |
ввод данных |
||||
i f |
((cin » |
numer » |
denom) == 0) |
break; |
|||||||
|
ret |
= fraction(numer, denom,ans,msg); |
/ / |
вычисление |
ответа |
||||||
i f |
(ret |
== 1) |
|
|
|
|
|
/ / |
допустимый |
ответ |
|
|
cout |
« |
"Value |
of |
the |
fraction; |
« ans |
« " \ n \ n " ; |
|
||
else i f |
(ret < 0) |
|
|
^'\n\r\''; |
} |
|
/ / |
отрицательное значение |
|||
|
cout |
« |
msg « |
ret |
« |
|
|||||
return 0; |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Если d'tseT — DBL_MAX, то функция inverseO обрабатывает ошибку с помощью |
|||||
|
|
|
вывода сообщения об ошибке или возврата нулевого значения, что указывает |
||||||
|
|
|
вызывающей программе об ошибке. Если параметр отрицательный, то функция |
||||||
|
|
|
inverseO |
возвращает значение, а клиент обнаруживает и обрабатывает ошибку. |
|||||
|
|
|
В ином случае inverseO возвращает 1, и это указывает вызывающей программе, |
||||||
|
|
|
что допустимо значение формального параметра answer. |
|
|||||
|
|
|
|
|
|
|
Функция f ractionO оценивает возвращаемое зна |
||
Enter |
numerator |
and |
positive |
|
чение inverseO. Если это значение |
1 (допустимый |
|||
denominator (any letter |
to |
quit): |
42 0 |
результат), она вычисляет значение дроби. Если воз |
|||||
Zero denominator |
is |
not |
allowed |
|
вращенное значение отрицательное |
(отрицательный |
|||
|
знаменатель), это значение передается своему собст |
||||||||
Enter |
numerator |
and |
positive |
|
|||||
42 -21 |
венному клиенту, и ему направляются дополнительные |
||||||||
denominator (any letter |
to |
quit): |
данные для обработки ошибки (выводится сообщение). |
||||||
|
|
|
|
|
|
|
|||
Negative denominator: -21 |
|
|
Клиентская программа оценивает значение, возвра |
||||||
Enter |
numerator |
and |
positive |
|
щенное f ractionO. Если это 1, то результат действи |
||||
|
тельный, и главная функция отображает результат. |
||||||||
denominator (any letter to quit): -42 21 |
|||||||||
Value |
of the fraction: |
|
-2 |
|
Если возвращаемое значение f ractionO отрицатель |
||||
Enter |
numerator |
and |
positive |
|
ное, клиентская программа выводит данное значение |
||||
exit |
и сообщение, полученное от fraction(). В противном |
||||||||
denominator (any |
letter |
to quit): |
|||||||
|
|
|
|
|
|
|
случае клиентская программа ничего не делает, по |
||
Рис. 18.2. Вывод |
программы |
скольку ошибка (нулевой знаменатель) уже была |
|||||||
|
из листинга |
18.2 |
|
обработана в inverse(). Результаты выполнения про |
|||||
|
|
|
|
|
|
|
граммы из листинга 18.2 показаны на рис. 18.2. |
||
|
|
|
|
Разделение мест обнаружения ошибки и исправления ошибки приводит к бо |
|||||
|
|
|
лее сложному решению. У серверных функций для использования имеются допол |
||||||
|
|
|
нительные возвращаемые значения и параметры. Прочная связь сторон вызывает |
||||||
|
|
|
сильную зависимость различных частей программы друг от друга. Клиентская |
||||||
|
|
|
программа должна подчиняться сложным соглашениям в отношении возвращае |
||||||
|
|
|
мых значений (в данном примере возвращение 1 обозначает допустимое значение |
||||||
|
|
|
параметра, возвращение нуля или отрицательного числа — неверное значение |
||||||
|
|
|
параметра) |
и вести |
себя по-разному для различных возвращаемых значений. |
804 |
Часть IV # Росширенноа использование C-t--^- |
функции inverseO. В случае отрицательного знаменателя требуется сообщить зна чение знаменателя, inverseO воспользуется обоими параметрами msg и возвращен ным ей значением для передачи информации своей вызывающей программой.
Исключительные ситуации С+Н- помогают обойтись без использования допол нительных параметров, возвращенных значений и сложных соглашений о вызовах.
Синтаксис исключительных ситуаций C+ +
Исключительные ситуации C+-f позволяют программистам изменить последо вательность передачи управления, когда возникает некоторое событие, например ошибка. Эти ошибки появляются во время выполнения (файл не найден, невер ный индекс и т. п.). Когда C + + провоцирует исключительную ситуацию и отсут ствует программа обработки, которой известно, как обработать это исключение, программа может завершиться.
Обработчики исключительных ситуаций являются частью исходного кода про граммы, который должен выполняться при возникновении исключительных ситуа ций (например, вывод сообщения пользователю, сбор информации для анализа причин возникновения исключительной ситуации или исправление ошибки).
Организация исходного кода обработки ошибок в обработчики исключитель ных ситуаций может сделать передачу управления более логичной. Вместо того, чтобы выполнять все проверки в основном алгоритме и скрыть его смысл, выпол нение обработки ошибок программируется в отдельной части. При таком подходе вы имеете возможность скрыть значения серверных функций, которые вовлечены в обнаружение ошибки.
Отдельная часть для исправления ошибки может находиться в том же методе, который вызвал исключительную ситуацию, в вызывающей программе данного метода, в вызывающей программе вызывающей программы и т. д. В подобном случае затрудняется проектирование с использованием исключительных ситуаций. Однако механизм исключительных ситуаций позволяет программисту передать управление для выполнения действий по исправлению организованным способом.
По-видимому, исключительные ситуации C + + позволяют программисту выде лить исключительные случаи в другие части исходной программы и упростить основную обработку. За счет этого программа становится удобочитаемой. Нельзя сказать, что это всегда так. Как уже отмечалось, использование исключительных ситуаций полезно потому, что исключаются дополнительные параметры, возвра щаемые значения и сложные соглашения о вызовах в функциях, которые выявля ют проблемы, и функциях, которые пытаются исправить недостаток.
Когда C++ провоцирует исключительную ситуацию, может быть создан объект предопределенного класса Exception или класса, определенного программистом. Этот класс, заданный программистом, может быть получен как производный от класса Exception или может быть независимым классом. В результате проектиро вание с исключительными ситуациями становится более сложным для понимания.
Исключительные ситуации могут быть сгенерированы явно в операторе throw или неявно как результат недопустимой или неверной операции. Исключительные ситуации отслеживаются оператором catch, а управление передается к оператору, отследившему исключение. Оператор отслеживания (или блок операторов) ис правляет ошибки. Передача управления после восстановления ошибки в блок отслеживания зависит от структуры программы. Обычно самопроизвольный возврат к месту возникновения исключительной ситуации не выполняется. Если же такое возвращение (т. е. продолжение обработки) желательно, программист должен организовать структуру программы специальным образом.
Собработкой исключительных ситуаций связаны три операции:
•Генерация исключительной ситуации
•Отслеживание исключительной ситуации
•Обозначение исключительной ситуации
Глава 18 • Программирование с обработкой исключительных ситуаций |
805 |
Генерация исключительной ситуации означает указание на то, что обнаружи ваются определенные исключительные (возможно, ошибочные) условия, которые должны быть обработаны с использованием механизма исключительных ситуаций C + + , а не стандартными методами передачи управления.
Отслеживание исключительной ситуации означает обозначение части про граммы, которая спроектирована для реагирования на некоторое конкретное исключительное условие.
Обозначение исключительной ситуации — это указание исключительной ситуа ции, которая может возникнуть в пределах данного метода. Это помогает компи лятору (и программистам, разрабатывающим клиентскую часть и осуществляющим сопровождение) узнать, что ожидать от функции и как она должна использоваться.
Генерация исключительной ситуации
Для генерации исключительной ситуации используется зарезервированное слово throw. Его применение показывает, что серверная программа обнаружила состояние, последовательность обработки которого ей неизвестна. Она генериру ет исключительную ситуацию, надеясь, что где-нибудь (в ее клиенте или клиентах клиента) найдется часть программы, которая знает способ обработки подобной ситуации.
Зарезервированное слово throw используется в операторе генерации исклю чительной ситуации. Общий синтаксический вид оператора включает зарезер вированное слово throw с операндом, который может быть значением любого типа, сгенерированным в процессе поиска программы обработки исключительной ситуации.
throw value;
Оператор throw обычно выполняется условно, после проверки каких-либо значений или связей в программе и при обнаружении, что они не отвечают требованиям. Это означает, что серверная программа выполняет оператор throw для уведомления клиента о проблемах, обнаруженных в серверной программе.
Оператор throw может содержать только один операнд любого типа. Однако некоторые компиляторы при попытке сгенерировать больше одного значения не помечают оператор throw как ошибку. Значение операнда throw используется клиентской программой, которая пытается обработать исключительную ситуацию для извлечения информации о контексте ошибки. Часто такая информация исполь зуется для определения поведения клиентской программы при исправлении ошибок.
Здесь приводится исправленный пример функции inverse(). В листинге 18.3 эта функция устанавливает возвращаемые значения или значения параметра для передачи в клиентскую программу. В этом варианте функция inverse() генерирует исключительные ситуации в двух случаях: во-первых, если обнаруживает, что знаменатель равен нулю, во-вторых, если видит, что знаменатель отрицательный.
inline void inverse(long value, clouble& answer) |
/ / |
два параметра |
||
{ answer = (value) |
? 1.0/value : DBL_MAX; |
|
|
|
i f |
(answer==DBL_MAX) |
|
|
|
|
throw MSG::msg(1); |
/ / |
нулевой знаменатель |
|
i f |
(value < 0) |
|
|
|
|
throw value; |
} |
/ / |
отрицательный знаменатель |
Понятно, что в случае нулевого знаменателя функция генерирует значение типа символьного массива, а в случае отрицательного знаменателя — значение типа long. Не случайно это разные типы. Было бы намного труднее обработать исключительную ситуацию, если бы оба оператора throw генерировали значения одного типа. Если обе исключительные ситуации должны обрабатываться одина ково, то это не проблема. Если исключительные ситуации должны обрабатываться
806 |
Часть IV • Расширенное использование С-^•4- |
|
|
|
||||||
|
по-разному, то клиентской программе потребовалось бы определять, что действи |
|||||||||
|
тельно произошло в серверной программе, которая сгенерировала исключитель |
|||||||||
|
ную ситуацию. |
|
|
|
|
|
|
|
|
|
|
Сравнивая функцию inverseO с ее версией |
в листинге 18.1, то видите, что |
||||||||
|
их интерфейсы похожи. Обе функции возвращ,ают пустой тип и имеют только два |
|||||||||
|
параметра. В листинге 18.1 функция inverseO |
не пытается обнаружить какие- |
||||||||
|
либо исключительные ситуации. Не делает это и ее клиент — f raction(). Кли |
|||||||||
|
ентская программа main() должна обнаружить обе исключительные ситуации |
|||||||||
|
(нулевой знаменатель, отрицательный знаменатель) и обработать их. |
|
||||||||
|
В листингах 18.2 и 18.3 функции inverseO и f ractionO пытались обнаружить |
|||||||||
|
исключительные ситуации, исправить некоторые из них (нулевой знаменатель) |
|||||||||
|
и дать возможность клиенту main() исправить остальные (отрицательный зна |
|||||||||
|
менатель). В результате получилась запутанная программа. Последний вариант |
|||||||||
|
inverse() генерирует обе исключительные ситуации. Он содержит некоторую часть, |
|||||||||
|
выполняющую анализ (чтобы решить, какие сгенерировать исключительные ситуа |
|||||||||
|
ции, если имеются), но интерфейс ее настолько же прост, как и для первого |
|||||||||
|
варианта в листинге 18.1. Здесь вам придется использовать дополнительную |
|||||||||
|
программу, которая была написана для отслеживания исключительных ситуаций. |
|||||||||
Отслеживание исключительной ситуации |
|
|
||||||||
|
у функции inverseO, которая может сгенерировать две исключительные си |
|||||||||
|
туации, есть прямой клиент — функция |
fractionO, |
вызываюш,ая |
inverseO, |
||||||
|
и косвенный клиент — функция main(), вызывающая f raction(). Иерархия вы |
|||||||||
|
зовов может быть произвольной. Если функция, в данном примере |
inverseO, |
||||||||
|
генерирует исключительную ситуацию и не обрабатывает ее, одна из ее вызыва- |
|||||||||
|
юш,их программ (прямых или косвенных) должна отслеживать эту исключитель |
|||||||||
|
ную ситуацию. |
|
|
|
|
|
|
|
|
|
|
Отслеживание |
исключительной |
ситуации |
— это процесс поиска програм |
||||||
|
мы, которая может обработать ошибку (программа обработки исключительных |
|||||||||
|
ситуаций). Для этого используется поиск по цепочке вызовов функции. |
|||||||||
|
Предположим, что для отслеживания исключительной ситуации требуется за |
|||||||||
|
резервированное слово catch. Действительно, в С+4- имеется зарезервированное |
|||||||||
|
слово catch, которое используется в отслеживании исключительной ситуации. |
|||||||||
|
Однако этого недостаточно. Когда функция выявляет исключительные ситуации, |
|||||||||
|
она не может отследить их из произвольного источника исключительных ситуаций. |
|||||||||
|
Функция должна указать, из какой части своей программы она будет пытаться |
|||||||||
|
выполнить отслеживание исключительных ситуаций. Для этого надо использовать |
|||||||||
|
еиде одно зарезервированное |
слово |
С+-\ |
try. Оно должно сопровождаться |
||||||
|
блоком, который может сгенерировать исключительные ситуации. |
|
||||||||
|
Программа клиента, отвечаюш.ая за отслеживание ошибок, включает код. Он |
|||||||||
|
может вызвать исключительную ситуацию в операторе |
try. |
|
|||||||
|
void foo() |
/ / |
функция, |
отслеживающая исключительные |
ситуации |
|||||
|
{ t r y |
|
/ / |
оператор |
try |
|
|
|
||
|
{ |
statements;} |
/ / |
операторы, |
генерирующие исключительные ситуации |
|||||
|
. . |
.} |
/ / |
остальная |
часть foo() с блоками отслеживания |
Программы обработки исключительных ситуаций в С+Н- реализуются с исполь зованием ключевых слов try и catch. Операторы (или вызовы метода), которые могут сгенерировать исключительные ситуации, помендаются в блоки try, а сами программы обработки исключительных ситуаций включаются в блоки catch.
За блоком try должны располагаться один или несколько блоков catch. Каж дый блок catch содержит параметр, соответствуюш,ий исключению, которое обра батывает этот блок.
Глава 18 • Программирование с обработкой исключительных ситуаций |
807 |
|||
void foo() |
/ / |
функция, отслеживающая исключительные |
ситуации |
|
{ t r y |
|
|
|
|
{ |
statements; } |
/ / |
операторы, генерирующие исключительные ситуации |
|
catch |
(Type1 t1) |
/ / |
блок отслеживания для генерации типа Туре1 |
|
{ handler_for_Type1(); |
} |
|
||
catch |
(Туре2 t2) |
/ / |
блок отслеживания для генерации типа Туре2 |
|
{ handler_for_Type2(); |
} |
|
||
catch |
(TypeN tN) |
/ / |
блок отслеживания для генерации типа TypeN |
|
{ handler_for_TypeN(); |
} |
|
statements_executed_after_the_try_or_catch_block; }
Вслед за оператором try должна следовать одна структура (блок) отслеживания, обеспечиваюидая обработку исключительных ситуаций. Использование блока catch, которому не предшествует оператор try, является ошибкой. (Все хорошо, если имеются другие блоки catch между этим и предшествуюш,им оператором try.) Использование оператора try, за которым отсутствует блок или блоки catch, является ошибкой.
Вспомним, что в операторе throw имеется аргумент некоторого типа — сим вольный массив, переменная long или даже значение некоторого типа класса, определенного программистом. Значение аргумента обычно содержит некоторую информацию о содержании ошибки. В случае функции inverseO эти данные, либо строка с выводимым сообш,ением, либо отрицательное значение знаменате ля, должны отображаться. Если оператор throw генерирует объект типа класса, то конструктор для него должен позволять объекту передавать некоторую инфор мацию о проблеме. Эта информация может использоваться конструктором catch для диагностики и исправления ошибки.
Если после блока try располагается несколько структур catch, то они должны иметь аргументы различных типов. Поскольку у структур catch имена отсутству ют, сигнатуры этих структур должны быть уникальными.
Если тип исключительной ситуации, сгенерированной в блоке try, "согласу ется" с аргументом структуры catch, выполняется программа структуры catch и поиск останавливается. После завершения работы блока выполняются опера торы, расположенные вслед за блоками catch для оператора try.
"Согласование аргумента" означает, что объект — исключительная ситуация, которая генерируется блоком try, может присваиваться параметру блока catch, означая точное соответствие, любые стандартные преобразования или любые параметры из подклассов структуры catch. Например, значение двойной длины можно отследить в блоке catch с параметром long, а объект SavingsAccount — в блоке catch с параметром Account.
После выполнения блока catch осуш.ествляются операторы, которые следуют за блоком try, и структуры catch. При необходимости эти операторы могут содер жать другие блоки try. Если оператор try не генерирует никакие исключительные ситуации, то структуры catch интерпретируются как операторы NULL. Они пропус каются.
Если исключительная ситуация была сгенерирована в середине оператора try, то выполнение оператора try завершается, находится и осуществляется структура catch и т.д. Операторы в блоке try, следуюи;ие за тем, который сгенерировал исключительную ситуацию, никогда не выполняются. Исключительная ситуация была сгенерирована потому, что эти операторы могут и не выполняться.
Что случится, если программа в блоке try генерирует исключительную ситуа цию, которая не содержит структуру catch соответствующего типа? Тогда функция завершается. Блок try и программа, которая следует за структурами catch, не выполняются. Это означает, что соответствующий блок catch будет находиться в клиентской программе этой функции. Если он обнаруживается, все хорошо.
808 |
Часть IV * Расширенное использование С-^+ |
Если структура catch, которая способна обработать исключительную ситуацию, не находится даже в main(), то выполнение программы завершается.
Рассмотрим следуюилий вариант функции inverseO, который генерирует иск лючительные ситуации и пытается отследить их.
i n l i ne |
void inverse(long value,double& answer) |
|
/ / два параметра |
|
{ t r y |
|
/ / |
начало |
блока try |
{ |
i f (value ==0) |
/ / |
нулевой |
знаменатель |
throw MSG::msg(1);
if (value < 0) |
|
/ / |
отрицательный знаменатель |
|
throw value; |
|
|
|
|
answer = 1.0 / value; } |
/ / |
конец блока try |
||
catch (char* str) |
|
|
|
|
{ cout « |
str; } |
|
/ / |
нулевой знаменатель |
catch (long val) |
|
|
/ / отрицательное значение |
|
{ cout « |
MSG::msg(2) |
« val « " \ n \ n " ; |
}} |
|
Если первый параметр |
имеет допустимое |
значение, блок try выполняется |
полностью, а блоки catch пропускаются. После блоков операторы отсутствуют, поэтому выполнение функции завершается.
Если первый аргумент равен нулю, генерируется исключительная ситуация для символьного массива и выполняется первый блок catch. Обратите внимание, что блок catch является "блоком". У него есть своя область видимости, и он ссылается на свой параметр str, а не на переменную, которая фактически была сгенерирована, MSG: :msg(1).
Подобным образом, если первый параметр отрицательный, генерируется его значение и выполняется второй блок catch. Снова имя выводимого значения — val, а не value. Независимо от того, какая исключительная ситуация генерируется, оператор answer = 1.0/value никогда не выполняется. Это разумно, т. к. данный оператор должен выполняться, только если значение проходит все проверки.
Если операторы в блоке try генерируют исключительную ситуацию, в которой отсутствует блок catch для ее обработки'в функции inverse(), то поиск програм мы обработки исключительной ситуации продолжается в fractionO, а затем
вmain().
Вданном варианте функции inverseO операторы thrown блоки catch находят ся в одной области действия функции. Синтаксически это допустимый вариант
вC+ + . Не нужно использовать механизм обработки исключительных ситуаций, если информация об этих ситуациях не передавалась по различным функциям. В данном случае простой оператор if в inverseO дает те же самые результаты.
Другая проблема обработки исключительной ситуации связана с выполнением оставшейся части программы. После завершения выполнения функции inverseO вызываюш,ие ее программы, fractionO и main(), не знают, появятся ли еш,ё какие-либо исключительные ситуации. Тем не менее, если какая-либо исключи тельная ситуация возникнет, то оператор, который вычисляет ответ, не выполня ется, и программы, вызываюш.ие inverseO, должны об этом знать.
Рассмотрим вариант inverseO, который генерирует исключительные ситуа ции, но не отслеживает их.
inline void |
inverse(long value,double& answer) |
/ / |
два параметра |
||
{ answer = (value) |
? 1.0/value : DBL_MAX; |
|
|
||
i f |
(answer==DBL_MAX) |
|
|
||
|
throw |
MSG::msg(1); |
/ / |
нулевой знаменатель |
|
i f |
(value |
< 0) |
|
|
|
|
throw value; |
} |
/ / |
отрицательный знаменатель |
Глава 18 • Программирование с обработкой исключительных ситуаций |
809 |
Отследим исключительные ситуации в клиентской функции f raction().
i n l i ne |
void |
fraction |
(long numer, |
long denom, |
double& |
result) |
|
|||
{ t r y |
{ |
|
|
|
|
|
|
|
|
|
inverse (denom, |
result); |
|
|
/ / |
result = 1.0 / denom |
|||||
result |
= numer |
* |
result; |
} |
|
/ / |
result ^ |
numer / denom |
||
catch (char* |
str) |
|
|
|
|
|
|
|
||
{ |
cout |
« |
str; |
} |
|
|
|
/ / |
нулевой |
знаменатель |
catch (long |
val) |
|
|
|
|
|
|
|
||
{ |
cout |
« |
MSG::msg(2) « |
val « |
"\n\n"; }} |
/ / |
отрицательное значение |
Этот вариант не лучше предыдущего варианта inverse(). Исключительные ситуа ции должны обрабатываться в таких местах клиентской программы, где информа ция о них может использоваться для изменения поведения программы (в данном случае пропуска отображения результата вычислений).
Влистинге 18.4 показан пример, как отмечалось ранее, искусственный, по скольку mainO может обнаружить, что ввод сам по себе неверный. Система об работки исключительных ситуаций имеет смысл: inverse() обнаруживает ошибку
иотправляет информацию main(), так что main() может пропустить использова ние неверных результатов.
Влистинге 18.4 функция inverseO анализирует происходяидее и генерирует две исключительные ситуации для своих вызываюш,их программ. Ее непосредст венно вызываюш,ая программа fractionO не содержит каких-либо программ обработки (структур catch), потому что она располагается в функции main(). Там же находится оператор, который должен быть пропуш^ен. Поскольку fractionO не содержит каких-либо структур catch, она не имеет оператора try также и пото му, что наличие оператора try без структур catch недопустимо.
Листинг 18.4. Пример генерации и отслеживания исключительных ситуаций
#inclucle <iostream> #include <cfloat> using namespace std;
class MSG {
static char* data[]; |
// внутренние статические данные |
public: |
// общедоступный статический метод |
static char* msg(int n) |
|
{ if (n<l II n > 5) |
// проверка допустимости индекса |
return data[0]; |
|
else |
// возвращение допустимой строки |
return data[n]; } |
|
char* MSG::data [] = { "\nBad argument to msg()\n", |
|
"\nZero denominator isnot allowed\n\n", |
// хранилище текста |
"\nNegative denominator: ", |
|
"Enter numerator and positive\n", |
|
"denominator (any letter toquit): ", "Value ofthe fraction: "
} ;
inline void inverse(long value, double& answer)
{ answer = (value) ? 1.0/value : DBL.MAX; if (answer==DBL_MAX)
throw MSG::msg(1); if (value < 0)
throw value; }