- •3. Структура главного файла проекта
- •1.8 Области видимости переменных и функций
- •1.8.1 Правила, определяющие область видимости
- •62 Глава 1
- •5. Обработка исключений в блоках try ... Catch
- •2.10.1 Файловый ввод/вывод с помощью компонентов
- •2.14.1 Объявление класса
- •186 Глава 2
- •188 Глава 2
- •244 Глава 3
5. Обработка исключений в блоках try ... Catch
Синтаксис блоков try ... catch
Наиболее кардинальный путь борьбы с исключениями — отлавливание и обработка их с помощью блоков try ... catch. Синтаксис этих блоков следующий:
try
<
Исполняемый код
}
catch ( TypeToCatch )
{
Код, исполняемый в случае ошибки
}
Операторы блока catch представляют собой обработчик исключения. Параметр TypeToCatch может быть или одним из целых типов (int, char и т.п.), или ссылкой на класс исключения, или многоточием, что означает обработку любых исключений. Смысл параметров целого типа будет рассмотрен ниже в разд. 1.12.6.1. А пока остановимся на случае, когда параметр является ссылкой на класс исключений.
Операторы обработчика выполняются только в случае генерации в операторах блока try исключения типа, указанного в заголовке catch. После блока try может следовать есколько блоков catch для разных типов исключений. Таким образом, в обработчиках catch вы можете предпринять какие-то действия: известить пользователя о возникшей проблеме и подсказать ему пути ее решения, принять какие-то меры к исправлению ошибки (например, при переполнении заслать в результат очень большое число соответствующего знака) и т.д. Наиболее ценным является то, что вы можете определить тип сгенерированного исключения и дифференцированно реагировать на различные исключительные ситуации. Причем перехват исключения блоком catch приводит к тому, что это исключение далее не обрабатывается стандартным образом, т.е. пользователю не предъявляется окно с непонятными ему английскими текстами.
Приведем пример обработки исключений. Пусть в вашем приложении имеется два окна редактирования Editl и Edit2, в которых пользователь вводит действительные числа типа float. Приложение должно разделить их одно на другое. При этом возможен ряд ошибок: пользователь может ввести в окно символы, не преобразуемые в целое число, может ввести слишком большое число, может ввести вместо делителя нуль, результат деления может быть слишком большим для типа float.
Следующий код отлавливает все эти ошибки:
float А;
try
{
А = StrToFloat(Editl->Text) / StrToFloat(Edit2->Text);
}
catch(EConvertError&)
{
Application->MessageBox("Вы ввели ошибочное число",
"Повторите ввод",МВ_ОК);
}
catch(EZeroDivide&)
{
Application->MessageBox("Вы ввели нуль",
"Повторите ввод",МВ__ОК) ;
}
catch(EOverflows)
{
Application->MessageBox("Переполнение",
"Ошибка вычислений",МВ_ОК);
if (StrToFloat(Editl->Text) * StrToFloat(Edit2->Text) >= 0)
A = 3.4E38;
else A = -3.4E38;
}
Если пользователь ввел неверное число (например, по ошибке нажал не цифру, а какой-то буквенный символ), то при выполнении функции StrToFloat возникнет исключение класса EConvertError. Соответствующий обработчик исключения сообщит пользователю о сделанной ошибке и посоветует повторить ввод.
Аналогичная реакция последует на ввод пользователем в качестве делителя нуля (класс исключения EZeroDivide). Если возникает переполнение, то соответствующий блок catch перехватывает исключение, сообщает о нем пользователю и исправляет ошибку: заносит в результат максимально возможное значение соответствующего знака.
Поскольку исключения образуют иерархию, рассмотренную в разд. 1.12.3, можно обрабатывать сразу некоторую совокупность исключений, производных от одного базового исключения. Для этого надо в заголовке блока catch указать имя этого базового исключения. Например, исключения EZeroDivide (целочисленное деление на нуль), EOverflow (переполнение при целочисленных операциях), ElnvalidArgument (выход числа за допустимый диапазон) и некоторые другие являются производными от класса исключений EMathError. Поэтому все их можно отлавливать с помощью одного блока catch, например, такого:
catch(EMathErrors)
{
Application->MessageBox ("Ошибка вычислений","Повторите ввод", MB_OK);
}
Правда, в этом случае не конкретизируется причина прерывания исключений. Однако такая конкретизация возможна, если воспользоваться свойствами исключений. Все исключения имеют свойство Message, которое представляет собой строку, отображаемую пользователю при стандартной обработке исключений.
Чтобы воспользоваться свойствами исключений, надо в заголовке блока catch не только указать тип исключения, но и создать временный указатель на объект этого типа. Тогда через имя этого объекта вы получаете доступ к его свойствам.
Ниже приведен пример использования свойств исключений при перехвате исключений, наследующих классу EMathError:
catch(EMathErrors E)
{
AnsiString S = "Ошибка вычислений : ";
if(E.Message == "EZeroDivide") S += "деление на нуль";
if(E.Message == "EOverflow") S += "переполнение";
if(E.Message == "EInvalidArgument") S += "недопустимое число";
Application->MessageBox(S.c_str(), "Повторите ввод", МВ_ОК);
}
Вводимое в этом операторе имя ссылки на исключение Е носит сугубо локальный характер и вводится только для того, чтобы можно было сослаться на свойство Message по имени объекта исключения.
Как уже говорилось выше, если в заголовке блока catch указано многоточие то этот блок перехватит любые исключения:
catch(...)
(
ShowMessage("Призошла ошибка.");
}
Блок catch(...) может сочетаться и с другими блоками catch, но в этом случае он должен, конечно, располагаться последним. Поскольку этот блок перехватит все исключения, то все блоки, следующие за ним, окажутся недоступными. C++Builder следит за этим. Если блок catch(...) оказался не последним, вам будет выдано компилятором сообщение об ошибке с текстом: "The handler must be last" ("Обработчик должен быть последним").
Следует отметить некоторую опасность применения блока catch(...). Перехват всех исключений способен замаскировать какие-то непредвиденные ошибки в программе, что затруднит их поиск и снизит надежность работы.
Файлы и потоки
Работа с файлами в C++Builder может производиться несколькими принципи-
ально различными (с точки зрения пользователя) способами:
• использование библиотечных компонентов
• работа с файлами как с потоками в стиле С
• работа с файлами как с потоками в стиле С++
Рассмотрим эти возможности.
