Russian Belarusian English German Japanese Ukrainian

Rashka.studio - игры и приложения для Android! Заходи, ждём тебя =)

Обработка исключений в блоках try ... catch

CuBook55

Наиболее кардинальный путь борьбы с исключениями это отлавливание и обработка их с помощью блоков try ... catch. Синтаксис этих блоков следующий:
try
{
Исполняемый код
}
catch ( TypeToCatch )
{
Код, исполняемый в случае ошибки
}
Операторы блока catch представляют собой обработчик исключения. Параметр TypeToCatch может быть или одним из целых типов (int, char и т.п.), или ссылкой на класс исключения, или многоточием, что означает обработку любых исключений. Остановимся на случае, когда параметр является ссылкой на класс исключений.
 
Операторы обработчика выполняются только в случае генерации в операторах блока try исключения типа, указанного в заголовке catch. После блока try может следовать несколько блоков catch для разных типов исключений. Таким образом, в обработчиках catch можно предпринять какие-то действия: известить пользователя о возникшей проблеме и подсказать ему пути ее решения, принять какие-то меры к исправлению ошибки (например, при переполнении заслать в результат очень большое число соответствующего знака) и т.д. Наиболее ценным является то, что можно определить тип сгенерированного исключения и дифференцированно реагировать на различные исключительные ситуации. Причем перехват исключения блоком catch приводит к тому, что это исключение далее не обрабатывается стандартным образом, т.е. пользователю не предъявляется окно с непонятными ему английскими текстами.
 
Пусть в приложении имеется два окна редактирования Edit1 и Edit2, в которых пользователь вводит действительные числа типа float. Приложение должно разделить их одно на другое. При этом возможен ряд ошибок: пользователь может ввести в окно символы, не преобразуемые в целое число, может ввести слишком большое число, может ввести вместо делителя нуль, результат деления может быть слишком большим для типа float. Следующий код отлавливает все эти ошибки:
float А;
try
{
A = StrToFloat(Edit1->Text) / StrToFloat(Edit2->Text);
}
catch(EConvertError&)
{
Application->MessageBox("Вы ввели ошибочное число", "Повторите ввод", MB_OK);
}
catch(EZeroDivide&)
{
Application->MessageBox ("Вы ввели нуль", "Повторите ввод", MB_OK);
}
catch(EOverflow&)
{
Application->MessageBox ("Переполнение", "Ошибка вычислений", MB_OK);
if(StrToHloat(Edit1->Text) * StrToFloat(Edit2->Text) >= 0)
А = 3.4E38;
else А = -3.4E38;
}
Если пользователь ввел неверное число (например, по ошибке нажал не цифру, а какой-то буквенный символ), то при выполнении функции StrToFloat возникнет исключение класса EConvertError. Соответствующий обработчик исключения сообщит пользователю о сделанной ошибке и посоветует повторить ввод. Аналогичная реакция последует на ввод пользователем в качестве делителя нуля (класс исключения EZeroDivide). Если возникает переполнение, то соответствующий блок catch перехватывает исключение, сообщает о нем пользователю и исправляет ошибку: заносит в результат максимально возможное значение соответствующего знака.
 
Поскольку исключения образуют иерархию, можно обрабатывать сразу некоторую совокупность исключений, производных от одного базового исключения. Для этого надо в заголовке блока catch указать имя этого базового исключения. Например, исключения EZeroDivide (целочисленное деление на нуль), EOverflow (переполнение при целочисленных операциях), EInvalidArgument (выход числа за допустимый диапазон) и некоторые другие являются производными от класса исключений EMathError. Поэтому все их можно отлавливать с помощью одного блока catch, например, такого:
catch(EMathError&)
{
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(), "Повторите ввод", MB_OK);
}
Вводимое в этом операторе имя ссылки на исключение E носит сугубо локальный характер и вводится только для того, чтобы можно было сослаться на свойство Message по имени объекта исключения.
 
Если в заголовке блока catch указано многоточие, то этот блок перехватит любые исключения: 
catch(...)
{
ShowMessage ("Произошла ошибка.");
}
Блок catch(...) может сочетаться и с другими блоками catch, но в этом случае он должен, конечно, располагаться последним. Поскольку этот блок перехватит все исключения, то все блоки, следующие за ним, окажутся недоступными. C++Builder следит за этим. Если блок catch(...) оказался не последним, то будет выдано компилятором сообщение об ошибке с текстом: «The handler must be last» («Обработчик должен быть последним»).
Следует указать на некоторую опасность применения блока catch(...). Перехват всех исключений способен замаскировать какие-то непредвиденные ошибки в программе, что затруднит их поиск и снизит надежность работы.
Блоки try...catch могут быть вложенными явным или неявным образом. Примером неявной вложенности является блок try...catch, в котором среди операторов раздела try имеются вызовы функций, которые имеют свои собственные блоки try...catch. При генерации исключения сначала ищется соответствующий ему обработчик в том блоке try...catch, в котором создалась исключительная ситуация. Если соответствующий обработчик не найден, поиск ведется в обрамляющем блоке try...catch (при наличии явным образом вложенных блоков) и т.д. Если в данной функции обработчик не найден или вообще в ней отсутствуют блоки try...catch, то поиск переходит на следующий уровень в блок, из которого была вызвана данная функция. Этот поиск продолжается по всем уровням. И только если он закончился безрезультатно, выполняется стандартная обработка исключения, заключающаяся, как уже было сказано, в выдаче пользователю сообщения о типе исключения.
 
Как только блок catch, соответствующий данному исключению, найден и выполнен, объект исключения разрушается и управление передается оператору, следующему за соответствующим блоком try...catch.
 
Если исключение не перехвачено ни одним обработчиком в функциях, можно обработать его на уровне приложения. Для этого предусмотрены события OnException компонента Application самого приложения. Обработчик этих событий можно ввести в приложение следующим образом. Допустим можно назвать этот обработчик MyException. Тогда в заголовочный файл приложения надо добавить его объявление:
void __fastcall MyException(TObject *Sender, Exception *E);
В файл вашего модуля надо внести реализацию обработчика:
void __fastcall TForm1::MyException(TObject *Sender, Exception *E)
{
// операторы обработки
}
Осталось указать приложению на функцию MyException как на обработчик события OnException. Можно это сделать, включив, например, в обработку события формы OnCreate оператор:
Applioation->OnException = MyException;
Обработчик не перехваченных ранее исключений готов. Осталось только наполнить его операторами, сообщающими пользователю о возникших неполадках и обеспечивающими дальнейшую работу программы. К функции MyException приложение будет обращаться, если было сгенерировано исключение и ни один блок catch его не перехватил. В функцию передается указатель Е на объект класса Exception. Этот объект является сгенерированным исключением, а класс Exception базовый класс всех исключений.
 
Простейшая обработка исключения могла бы производиться функцией ShowException, обеспечивающей отображение информации об исключении:
Application->ShowException(Е);
В заголовке окна пишется имя приложения, а текст содержит описание причины генерации исключения. Основным недостатком функции являются сообщения на английском языке, что вряд ли порадует пользователей приложения. Поэтому лучше сделать собственные сообщения. При этом для определения истинного класса сгенерированного исключения можно воспользоваться методом ClassName. Тогда обработчик события OnException может иметь, например, следующий вид:
void __fastcall TForm1::MyException(TObject *Sender, Exception *E)
{
AnsiString S = "Ошибка вычислений : ";
if(String(E->ClassName{}) == "EZeroDivide") S += "деление на нуль";
if(String(E->ClassName()) == "EOverflow") S += "переполнение";
if(String(E->ClassName()) == "EInvalidArgument") S += "недопустимое число";
if(String(E->ClassName()) == "EConvertError") S += "ввели недопустимое число";
Application->MessageBox(S.c_str(),"Повторите ввод",MB_OK);
}
Если заметили ошибку, выделите фрагмент текста и нажмите Ctrl+Enter

Добавить комментарий