Наиболее кардинальный путь борьбы с исключениями это отлавливание и обработка их с помощью блоков 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(E);
В заголовке окна пишется имя приложения, а текст содержит описание причины генерации исключения. Основным недостатком функции являются сообщения на английском языке, что вряд ли порадует пользователей приложения. Поэтому лучше сделать собственные сообщения. При этом для определения истинного класса сгенерированного исключения можно воспользоваться методом 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);
}
Табаков Юрий
Программист
Автор и редактор проекта CuBook.PRO. Главная задача, которую я ставлю перед собой – донести до начинающих программистов удобочитаемый материал. Буду рад выслушать замечания и предложения. Не забываем ставить оценки и делать репосты =)
Сообщение было отмечено как решение
Решение
Сообщение от Programmer.
Добрый вечер форум
Может кто нит обияснит что такое try, catch(…)
Принципы обработки исключений
В С++ исключение — это объект. Хотя обычно говорят об исключительной ситуации в программе, такая точка зрения мало что дает, так как с ситуацией сделать ничего нельзя. Поэтому в С++ при возникновении исключительной ситуации программа должна генерировать объект-исключение. Собственно, сама генерация объекта-исключения и создает исключительную ситуацию. Такой подход очень удобен, так как с объектами, в отличие от ситуаций, мы можем много чего делать. Например, объект-исключение можно объявить как обычную переменную, передать его как параметр любым из возможных способов или возвратить в качестве результата. Можно объявлять массивы исключений или включать объекты-исключения в качестве полей в другие классы. В дальнейшем мы будем использовать термин «исключение», понимая под этим объект-исключение.
Общая схема обработки исключений такова: в одной части программы, где обнаружена аварийная ситуация, исключение порождается; другая часть программы контролирует возникновение исключения, перехватывает и обрабатывает его. В С++ есть три зарезервированных слова: try (контролировать), catch (перехватывать), throw (порождать), — которые и используются для организации процесса обработки исключений.
Генерация исключений
Генерируется исключение оператором throw (см. п. 15.1 в [1]), который имеет следующий синтаксис
throw выражение_генерации_исключения;
Выражение генерации исключения на практике означает либо константу, либо переменную некоторого типа. Тип объекта-исключения может быть любым, как встроенным, так и определяемым программистом. Например:
C++ | ||
|
В первом случае объект-исключение — это целая константа, которая может быть условным номером-кодом ошибки. В общем случае этот код ошибки может вычисляться, например:
Выражение, конечно, может быть не только целочисленным.
Второй вариант — это символьная константа, которая фактически является сообщением об ошибке. Все сообщения об ошибках могут быть записаны в массиве, например:
C++ | ||
|
Тогда в третьем варианте объект-исключение тоже представляет собой строку — сообщение об ошибке. В последнем примере в качестве объекта используется дробная константа — число *.
Программист может и сам определить собственный тип объекта-исключения, объявив новый класс, например:
C++ | ||
|
Пустые классы неожиданно пригодились! Однако объявлять переменную совсем не обязательно, можно обойтись и без этого, например:
C++ | ||
|
Здесь в качестве выражения генерации исключения мы использовали явный вызов конструктора без аргументов, и это наиболее распространенная форма генерации исключения.
Объект-исключение может быть и динамическим, например:
C++ | ||
|
Однако в этом случае возникает вопрос об уничтожении объекта-исключения и возврате памяти — неизвестно, в каком месте программы это можно сделать. Лучше не создавать себе проблем и не использовать указатели в качестве параметров блоков catch.
Перехват и обработка исключений
Сгенерировать исключение — это только полдела. Как уже отмечалось, исключение надо перехватить и обработать. Проверка возникновения исключения делается с помощью оператора try, с которым неразрывно связаны одна или несколько блоков обработки исключений — catch. Оператор try объявляет в любом месте программы контролируемый блок, который имеет следующий вид:
C++ | ||
|
Контролируемый блок, помимо функции контроля, обладает функциями обычного блока: все переменные, объявленные внутри него, являются локальными в этом блоке и не видны вне его.
После блока try обязательно прописывается один или несколько блоков catch, которые обычно называют обработчиками исключений, или секциями-ловушками. Форма записи секции-ловушки следующая:
C++ | ||
|
Спецификация параметра исключения может иметь следующие три формы:
(тип имя)
(тип)
(…)
Тип должен быть одним из допустимых типов исключений — либо встроенным, либо определенным программистом . Первый вариант спецификации означает, что объект-исключение передается в блок обработки, чтобы там его как-то использовать, например, для вывода информации в сообщении об ошибке.
ВНИМАНИЕ
При выполнении оператора throw создается временный объект-исключение, который и передается в секцию-ловушку (см. п.п. 15.1/3 в [1]).
При этом объект-исключение может передаваться в секцию-ловушку любым способом: по значению, по ссылке или по указателю, например:
C++ | ||
|
Однако в отличие от параметров функций, никаких преобразований типов по умолчанию не производится . Это означает, что если в программе написан следующий заголовок обработчика, то попадают в соответствующую ловушку только те исключения, тип которых совпадает с double:
C++ | ||
|
Еще пример:
Этот оператор генерирует исключение целого типа, поэтому будет обрабатываться секцией-ловушкой с заголовком, в котором прописан целый тип исключения, например, одним из следующих способов:
C++ | ||
|
Это легко проверить, выполнив следующую простую тестовую программу:
C++ | ||
|
На экране появится слово integer, так как константа 1 по умолчанию имеет тип int.
Первые две формы из приведенных спецификаций параметра исключения предназначены для обработки конкретного типа исключений. Если же на месте спецификации исключения написано многоточие (как в функциях с переменным числом параметров), то такой обработчик перехватывает все исключения.
Работа конструкции try…catch напоминает работу оператора switch. Секции-ловушки похожи на альтернативы case, а блок catch с многоточием соответствует альтернативе default оператора-переключателя. Если в блоке try возникает исключение, то начинается сравнение типа сгенерированного исключения и типов параметров в секциях-ловушках. Выполняется тот блок catch, тип параметра которого совпал с типом исключения. Если такого типа не найдено, выполняется блок catch с многоточием. Если же такого блока в текущем списке обработчиков не обнаруживается, то ищется другой список обработчиков в вызывающей функции. Этот поиск продолжается вплоть до функции main(). Если же и там не обнаружится нужного блока catch, то вызывается стандартная функция завершения terminate() (см. п.п. 15.5.1 в [1]), которая вызывает функцию abort().
Таким образом, очень важен порядок записи секций-ловушек после контролируемого блока. Если в качестве первого обработчика после блока try задан блок catch с параметром-многоточием, то такой обработчик будет обрабатывать все возникающие исключения — до остальных секций-ловушек дело просто не дойдет. Поэтому усвойте следующее простое правило: блок catch с параметром-многоточием всегда должен быть последним. Секция-ловушка с многоточием — это крайняя мера: если уж мы в нее попали, то в программе произошло что-то совсем непредусмотренное. Поэтому в такой секции обычно выводят сообщение о непредвиденном исключении и завершают работу программы.
24
Исключения и их стандартная обработка
При работе программы
могут возникать различного рода ошибки:
переполнение, деление на нуль, попытка
открыть несуществующий файл и т.п. В
этих случая, т.е. когда программа не
может выполнить какое-то действие. При
возникновении таких исключительных
ситуаций программа генерирует так
называемое исключение
и выполнение
дальнейших вычислений в данном блоке
прекращается. Исключение — это объект
специального вида, характеризующий
возникшую в программе исключительную
ситуацию. Каждому виду исключения
соответствует свой класс. Объект,
созданный на основе такого класса может
содержать в виде параметров некоторую
уточняющую информацию. Особенностью
исключений является то, что это сугубо
временные объекты. Как только они
обработаны каким-то обработчиком, они
разрушаются.
Если исключение
не перехвачено, то оно обрабатывается
методом Application->HandleException.
Он обеспечивает
стандартную реакцию программы на
большинство исключений — выдачу
пользователю краткой информации в окне
сообщений и уничтожение экземпляра
исключения.
Исключения могут
быть созданы не только автоматически,
т.е. самой программой, но и программистом.
Базовый класс исключений vcl Exception
Все предопределенные
в C++Builder классы исключений являются
прямыми или косвенными наследниками
класса Exception,
объявленного
в модуле SysUtils
и наследующего непосредственно TObject.
Класс
Exception
имеет два
свойства:
Свойство |
Тип |
Описание |
HelpContext |
int |
Целый |
Message |
System::AnsiString |
Строка |
Свойство Message
имеет значение
по умолчанию, которое присваивается
при автоматической генерации исключения.
При преднамеренной генерации исключений
их конструкторы, могут задавать значение
свойства Message
в виде переменной типа string
или литеральной константы.
Свойство HelpContext
хранит целый
идентификатор соответствующий экрану
контекстно-зависимой справки. Этот
экран справки отображается, если
пользователь, находясь в окне с сообщением
об ошибке, нажимает клавишу Fl .
По умолчанию
значение свойства HelpContext
равно 0. Это
значение может изменяться некоторыми
конструкторами исключений. Например,
оператор
throw
Exception(«He
хватает исходных данных», 4 );
генерирует
исключение со значением свойства
Message,
равным тексту «Не хватает исходных
данных», и значением свойства HelpContext,
равным 4. При
получении сообщения об этом исключении
пользователь сможет нажать клавишу F1
и получить пояснения, что ему делать в
этом случае. Генерация исключения
осуществляется ключевым словом throw.
Пользователь
может, проанализировав какие-то данные
и обнаружив ошибку, сгенерировать таким
образом собственное исключение, которое
будет затем обработано приложением.
Чтобы свойство
HelpContext
работало,
надо создать соответствующий файл
справки и связать его с приложением,
установив соответствующую опцию Help
file (файл справки) в окне Project Options (опции
проекта) на странице Application (приложение).
Обработка
исключений в блоках try
… catch
Рассмотрим следующий
пример. В
приложении имеется два окна редактирования
Edit1
и
Edit2,
в которых
пользователь вводит действительные
числа типа float.
Приложение
должно разделить их одно на другое.
Выполняемый код
очень простой:
float
A;
A=StrToFloat(Edit1->Text)/StrToFloat(Edit2->Text);
Если в Edit2
поместить ноль, то после запуска программы
появится сообщение
Если в любом окне
вместе с цифрами закрадется символ, то
появится сообщение
Эти сообщения
предупреждают пользователя о том, что
произошла исключительная ситуация.
Недостатком такого хода выполненеия
программы, во-первых, является ее
завершение, после нажатия кнопки OK,
во-вторых, предложения на английском
языке, что многим пользователям непонятно.
Очевидно, что при
выполнении программы произошла
исключительная ситуация или, как говорят,
исключение. Если бы в программу поступило
сообщение о том, что произошло, то можно
было сделать так, что программа вышла
из исключительной ситуации.
В языке С++ имеется
множество классов, которые создают
соответствующие объекты в момент
возникновения исключительной ситуации.
Эти объекты, также как и исключительные
ситуации, называются исключениями.
Путаницы из-за одинакового имени разных
понятий, обычно, не возникает, т.к. из
контекста понятно о чем идет речь.
Наиболее кардинальный
путь борьбы с исключениями — отлавливание
и обработка их с помощью блоков try
… catch.
Синтаксис
этих блоков следующий:
try
{
Исполняемый
код в котором возможны исключения
}
catch
(Тип_соответствующего_исключения)
{
Код,
исполняемый в случае ошибки
}
Операторы блока
catch
представляют
собой обработчик исключения. Параметр
Тип_соответствующего_исключения
может быть
или одним из целых типов (int,
char
и т.п.), или
ссылкой на класс исключения, или
многоточием, что означает обработку
любых исключений. Остановимся на случае,
когда параметр является ссылкой на
класс исключений.
Операторы обработчика
выполняются только в случае генерации
в операторах блока try
исключения типа, указанного в заголовке
catch.
После блока
try
может следовать несколько блоков catch
для разных
типов исключений. Таким образом, в
обработчиках catch
можно
предпринять какие-то действия: известить
пользователя о возникшей проблеме и
подсказать ему пути ее решения, принять
какие-то меры к исправлению ошибки
(например, при переполнении заслать в
результат очень большое число
соответствующего знака) и т.д. Наиболее
ценным является то, что вы можете
определить тип сгенерированного
исключения и дифференцированно
реагировать на,различные исключительные
ситуации. Причем перехват исключения
блоком catch
приводит к
тому, что это исключение далее не
обрабатывается стандартным образом,
т.е. пользователю не предъявляется окно
с непонятными ему английскими текстами.
Приведем пример
обработки исключений. Пусть в вашем
приложении имеется два окна редактирования
Edit1
и
Edit2,
в которых
пользователь вводит действительные
числа типа float.
Приложение
должно разделить их одно на другое. При
этомвозможен ряд ошибок: пользователь
может ввести в окно символы, не
преобразуемые в целое число, может
ввести слишком большое число, может
ввести вместо делителя нуль, результат
деления может быть слишком большим для
типа float.
Следующий
код отлавливает все эти ошибки:
float
A;
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
(StrToFloat (Edit1->Text) * StrToFloat (Edit2->Text) >= 0)
A
= 3.4E38;
else
A
= -3.4E38;
}
Если пользователь
ввел неверное число (например, по ошибке
нажал не цифру, а какой-то буквенный
символ), то при выполнении функции
StrToFloat
возникнет
исключение класса EConvertError.
Соответствующий
обработчик исклюю чения сообщит
пользователю о сделанной ошибке и
посоветует повторить ввод. Аналогичная
реакция последует на ввод пользователем
в качестве делителя нуля (класс исключения
EZeroDivide).
Если возникает
переполнение, то соответствующий блок
catch
перехватывает
исключение, сообщает о нем пользователю
и исправляет ошибку: заносит в результат
максимально возможное значение
соответствующего знака.
Поскольку исключения
образуют иерархию, можно обрабатывать
сразу некоторую совокупность
исключений,производных от одного
базового исключения. Для этого надо в
заголовке блока catch
указать имя
этого базового исключения. Например,
исключения EZeroDivide
(целочисленное
деление на нуль), EOverflow
(переполнение
при целочисленных операциях),
EInvalidArgument
(выход числа
за допустимый диапазон) и некоторые
другие являются производными от класса
исключений EMathError.
Поэтому все
их можно отлавливать с помощью одного
блока catch,
например, такого:
catch
(EMathError&)
{
Application->MessageBox(«Ошибка
вычислений», «Повторите ввод»,
МВ_ОК) ;
}
Правда, в этом
случае не конкретизируется причина
прерывания исключений. Однако такая
конкретизация возможна, если воспользоваться
свойствами исключений. Все исключения
имеют свойство Message,
которое
представляет собой строку, отображаемую
пользователю при стандартной обработке
исключений.
Чтобы
воспользоваться свойствами исключений,
надо в заголовке блока catch
не только
указать тип исключения, но и создать
временный указатель на объект этого
типа. Тогда через имя этого объекта вы
получаете доступ к его свойствам. Ниже
приведен пример использования свойств
исключений при перехвате исключений,
наследующих классу EMathError:
catch
(EMathError& 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);
}
Вводимое в этом
операторе имя ссылки на исключение Е
носит сугубо локальный характер и
вводится только для того, чтобы можно
было сослаться на свойство 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 TEorm1::MyException(TObject *Sender, Exception *E)
{
//
операторы
обработки
}
Осталось
указать приложению на вашу функцию
MyException
как на
обработчик события OnException.
Вы можете
это сделать, включив, например, в обработку
события формы OnCreate
оператор:
Application->OnException
= MyException;
Обработчик не
перехваченных ранее исключений готов.
Осталось только наполнить его операторами,
сообщающими пользователю о возникших
неполадках и обеспечивающими дальнейшую
работу программы. К функции MyException
приложение
будет обращаться, если было сгенерировано
исключение и ни один блок catch
его не
перехватил. В функцию передается
указатель Е
на объект класса Exception.
Этот объект
является сгенерированным исключением,
а класс Exception
— базовый
класс всех исключений. Простейшая
обработка исключения могла бы производиться
функцией ShowException,
обеспечивающей
отображение информации об исключении:
Application->ShowException(E);
В заголовке окна
пишется имя приложения, а текст содержит
описание причины генерации исключения.
Основным недостатком функции являются
сообщения на английском языке, что вряд
ли порадует пользователей вашего
приложения. Поэтому лучше сделать
собственные сообщения. При этом для
определения истинного класса
сгенерированного исключения можно
воспользоваться методом 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);
}
В C++Builder имеется
исключение EAbort,
несколько
отличающееся от рассмотренных ранее.
Генерация этого исключения, как и любых
других, прерывает процесс вычисления.
Но если приложение не отлавливает
соответствующим блоком catch
исключений этого класса, то они попадают
в обработчик TApplication::HandleException
и там, в
отличие от других исключений, разрушаются
без всяких сообщений. Таким образом,
это «молчаливое» прерывание процесса
вычисления, при котором не должно
отображаться диалоговое окно с сообщением
обошибке. Простейший путь генерации
исключения EAbort
— вызов
функции Abort.
Например:
if
(…) Abort( );
Только нельзя
путать две похожие внешне функции: Abort
– генерация
“молчаливого” исключения, и abort
— аварийное
завершение программы. Обычное применение
EAbort
— прерывание
вычислений при выполнении некоторого
условия окончания или условия прерывания
пользователем (например, при нажатии
клавиши Esc или какого-то оговоренного
сочетания клавиш). Функция Abort
прерывает
текущую процедуру и все вызвавшие ее
процедуры, передавая управление на
самый верх. Таким образом, это наиболее
простой выход из глубоко вложенных
процедур. Впрочем, можно при необходимости
перехватить исключение на каком-то
промежуточном уровне, предусмотрев на
нем блок try…catch
и вставив
соответствующий оператор обработки:
catch(EAbort&)
Рассмотрим простой пример
//Простой пример
обработки исключительной ситуации
#include<iostream>
#include<windows>
using
namespace std;
int main(){
SetConsoleOutputCP(1251);
cout<<«Начало«<<endl;
try{//начало
блока
try
cout<<«Внутри
блока
try»<<endl;
throw
10;//возбуждение
ошибки
cout<<«Эта
инструкция выполнена не будет, ошибка
уже произошла»;
}catch(int
i){//перехват
ошибки
cout<<«Перехвачена
ошибка номер: «<<i<<endl;
}
return 0;
char z;
cin>>z;
}
В этом прмере ошибка генерируется
всегда, что совсем не характерно для
реального процесса программирования.
Цель этого примера состоит в том, чтобы
показать каким образои можно сгенерировать
и перехватить ошибку.
Глядя на этот пример можно сказать, что
в блоке try,в случае возникновения исключительной
ситуации, операторthrowопределяет объект для передачи в блокcatch,
выполнение блокаtryпрекращается, т.е. остаток блока
игнорируется. Далее выполнение программы
переходит в блокcatch.
Сформированный в блокеtryобъект , в данном случае это число типаintравное 10, передается блокуcatchв качестве параметра. Блоковcatch
может быть несколько, в зависимости
от числа генерируемых исключений. Но у
каждого блокаtryолжен быть хотя бы один блокcatchи наоборот.
Как уже упоминалось, тип
исключительной ситуации должен
соответствен типу, заданному в инструкции
catch.
Например, в предыдущем примере, ее
изменить тип данных в инструкции catch
на double,
то исключительная туация
не будет перехвачена и будет иметь место
ненормальное завершен программы.
Исключительная ситуация
может быть возбуждена не входящей в
блок try
инструкцией,
если сама эта инструкция входит в
функцию, которая вызывается из
блока try.
Например, ниже представлена совершенно
правильная программа:
/*Возбуждение
исключительной ситуации из функции,
находящейся
вне
блока
try
*/
#include
<iostream>
#include<windows>
using
namespace
std;
void
Xtest(int test){
cout
<< «Внутри
функции
Xtest, test равно:
» << test << «n»; if(test)
throw test;
}
int
main (){
SetConsoleOutputCP(1251);
cout
<< «началоn»;
try
{ //
начало
блока
try
cout
<<«Внутри
блока
tryn»;
Xtest(0);
Xtest
(1);
Xtest
(2);
}
catch
(int i) { // перехват
ошибки
cout
<< «перехвачена ошибка номер: «;
cout
<< i
<< «n»;
}
cout
<<
«конец»;
return
0;
}
Следует напомнить, что C++
поддерживает синтаксис языка «С», в
которомfalseзаписывается как 0 , а все, что не ноль
естьtrue.
Именно поэтому в функции возникает
исключение.
Блок try
можно располагать внутри функции. В
этом случае при каждом входе
в функцию обработчик исключительной
ситуации устанавливается снова. Например,
//
Блоки try
и catch
могут находиться не только в функции
main()
#include
<iostream>
using
namespace std;
void
Xhandler(int test){
try
{
if(test)
throw test;
catch(int
i) {
cout
<< «перехвачена ошибка номер:<<i<<endl;
}
}
//=====================
int
main(){
cout<<«началоn»;
Xhandler
(1);
Xhandler
(2);
Xhandler(0);
Xhandler(3);
cout<<«конец»;
return
0;
}
На экран
программа выводит следующее:
начало
Перехвачена
ошибка номер: 1
Перехвачена
ошибка номер: 2
Перехвачена
ошибка номер: 3
конец
В данном
примере обработаны три исключительные
ситуации. После вызова каждой
исключительной ситуации функция
возвращает свое значение. При повторном
вызове функции обработчик исключительной
ситуации устанавлив ется вновь.
Как упоминалось ранее, с
блоком try
можно связать более одной инструкции
catch.
Как правило, так и делается. При этом
каждая инструкция catch
предназначена для перехвата своего
типа исключттельной ситуации
#include<iostream>
#include<windows>
using
namespace
std;
//Можно
перехватывать разные типы исключительных
ситуаций
void
Xhandler(int
test){
try{
if(test)
throw test;
else
throw «Значение
равно
нулю»;
}
catch(int
i) {
cout
<<«Перехвачена
ошибка
номер:
«<<i<<endl;
}
catch(char
*str) {
cout
<<«Перехвачена
строка:
»
<<str << endl;
}
}
//======================
main(){
SetConsoleOutputCP(1251);
cout
<< «началоn»;
Xhandler(1);
Xhandler(2);
Xhandler(0);
Xhandler(3);
cout
<< «конец»;
return
0;
}
На экран
программа выводит следующее:
Как видите, каждая инструкция
catch
перехватывает только
исключительные ситуации соответствующего
ей типа.
Обычно выражения инструкций
catch
проверяются в
том порядке, в кoтopoм
они появляются в программе. Выполняется
только та инструкция, которая совпадает
по типу даниых с исключительной ситуации.
Все оетальные блоки catch
игнорируются.
Исключения
с Builder
Пример для Builder
Создайте форму
с
обработчиком
кнопки
void
__fastcall TForm1::Button1Click(TObject *Sender)
{
float
a;
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(StrToFloat(Edit1->Text)*StrToFloat(Edit2->Text)>=0)
a=3.4E38;
else
a=-3.4E38;
}
}
//—————————————————————————
Отключите дебаггер. Для
этго войдите в меню Tools/Debugger
Options.
В
появившемся
окне
снимите
флажок
Integrated
debugging.
Лена |
Отправлено: 04.10.2006, 12:02 |
||||
Мастер участка Группа: Участник |
В базе данных есть колонки с типом timestamp (время и дата). Если пользователь вводит не правильно данные в эти колонки (в гриде), то возникает исключение. Как правильно перехватить и обработать такое исключение: Связь: ADOQuery -> TDatasetProvider -> TClientDataset -> TDatasource Для провайдера написала обработчик, но он не срабатывает:
|
||||
olegenty |
Отправлено: 04.10.2006, 12:31 |
||||
Ветеран Группа: Модератор |
typedef void __fastcall (__closure *TFieldNotifyEvent)(TField* Sender); __property TFieldNotifyEvent OnValidate = {read=FOnValidate, write=FOnValidate}; Description Write an OnValidate event handler to validate changes made to the data in the field, just before the data is written to the current record buffer. The EditMask property allows validation of the data on a character by character basis while it is being entered by the user. OnValidate allows an application to validate the data as a whole. |
||||
Лена |
Отправлено: 04.10.2006, 12:48 |
||||
Мастер участка Группа: Участник |
Написала для нужного поля, никакой реакции. Все равно исключение:
|
||||
Admin |
Отправлено: 04.10.2006, 15:24 |
||||
Владимир Группа: Администратор |
Конечно обычный DBGrid не удобен для ввода/редактирования значений полей.
Все-же для исключения обычных ошибок ввода служат
Тоже самое и с другими данными — целыми и дробными числами, —
Самое простое(но не самое правильное) — взять компонент
наверное более правильным будет вот так:
а задать предварительные ограничения на значения полей
Можно прочитать также здесь: |
||||
Лена |
Отправлено: 04.10.2006, 17:14 |
||||
Мастер участка Группа: Участник |
Что-то в моем случае не удается сделать проверку. В поле ввода дат ввела маску !00/00/0000;1;_ Реально там такие значения: 08.12.1999 Теперь осталось проверить допустимые значения. Объясните, пожалуйста, почему не срабатывает обработчик OnValidate ниже:
Код:
Отредактировано Лена — 04.10.2006, 17:16 |
||||
Admin |
Отправлено: 04.10.2006, 17:43 |
||||
Владимир Группа: Администратор |
А приведите код ? Где вы в OnSetText делаете проверку
(в приведенном примере эта проверка выполняется
|
||||
Лена |
Отправлено: 04.10.2006, 18:08 |
||||
Мастер участка Группа: Участник |
>А приведите код ? Где вы в OnSetText делаете проверку
На «ты» Я там вписываю то же что и в OnValidate:
Что касается кода:
Отредактировано Лена — 04.10.2006, 18:10 |
||||
Admin |
Отправлено: 04.10.2006, 18:18 |
||||
Владимир Группа: Администратор |
Так а зачем от него избавляться-то ? Ведь тогда сразу это исключение перехватит catch, в котором и можно предупредить о неправильном вводе в конкретном поле.
И никаких других окошек/исключений не будет. |
||||
Admin |
Отправлено: 04.10.2006, 18:26 |
||||
Владимир Группа: Администратор |
Sender->Value в OnSetText работать не должно. Значение полю там еще не присвоено, оно только в Text !
Если значение правильно — его необходимо там присвоить |
||||
Лена |
Отправлено: 04.10.2006, 18:36 |
||||
Мастер участка Группа: Участник |
Понятно. Оставлю IDE исключение. Просто мне удалось избавиться от исключения IDE когда требуется обязательный ввод значений. Я реализовала так:
Пока пользователь не введет значения будет при переходе на новую строку получать ShowMessage. Отредактировано Лена — 04.10.2006, 18:38 |
||||
Admin |
Отправлено: 04.10.2006, 18:45 |
||||
Владимир Группа: Администратор |
«Понятно. Оставлю IDE исключение»
Что за IDE исключение ???
Если выскакивает какое-то еще окошко с исключением, то это |
||||
Лена |
Отправлено: 04.10.2006, 18:49 |
||||
Мастер участка Группа: Участник |
Вот то единственное исключение которое происходит до сработки catch. Оно на строке: TDateTime dtDate = StrToDate(Text);
P.S. Да без среды IDE все нормально — исключения нет. Просто думала избавиться от него и в период разработки. Отредактировано Лена — 04.10.2006, 18:51 Присоединить изображение |
||||
Admin |
Отправлено: 04.10.2006, 18:51 |
||||
Владимир Группа: Администратор |
Так вот об этом я и пишу, что запустив программу не из среды C++Builder этого исключения не будет ! Только сообщение ShowMessage из catch |
||||
Admin |
Отправлено: 04.10.2006, 18:55 |
||||
Владимир Группа: Администратор |
А в период разработки тоже избавиться не сложно — это галочка в опциях проекта, не помню сейчас где, и эти исключения тоже не будут появляться из среды разработки. |
||||
Лена |
Отправлено: 04.10.2006, 19:00 |
||||
Мастер участка Группа: Участник |
Ясно.
P/S/
|
||||
Admin |
Отправлено: 04.10.2006, 19:44 |
||||
Владимир Группа: Администратор |
Если я правильно понял: (этот документ уже советовал изучить) http://dchumichkin.narod.ru/bookdatabase/glava_06.html то событие OnValidate происходит уже после события OnSetText.
То есть последовательность событий следующая:
Оба события наступают до Post(), но отличаются тем, что в событии
То есть — поскольку значение даты было в корне неверно,
Попробуйте ввести допустимое значения (с точки зрения формата
В нем можно будет проверить, например допустимый диапазон дат,
|
||||
Лена |
Отправлено: 04.10.2006, 21:23 |
||||
Мастер участка Группа: Участник |
Огромное спасибо! Теперь все понятно! | ||||
Обработка исключительных ситуаций
, обработка ошибок
- Подписаться на тему
- Сообщить другу
- Скачать/распечатать тему
|
|
Junior Рейтинг (т): нет |
Прочитал много литературы по обработке исключительных ситуаций, но не могу понят ь до конца как использовать все это? int i=0; i=i/i;
вызывается исключение EDivByZero |
sten_11 |
|
Senior Member Рейтинг (т): 13 |
int i=0; int a=10; try { double b= a/i; } catch(…) {ShowMessage(«Не могу выполнить операцию»);} |
forward89 |
|
Junior Рейтинг (т): нет |
Цитата sten_11 @ 08.08.07, 12:25 int i=0; int a=10; try { double b= a/i; } catch(…) {ShowMessage(«Не могу выполнить операцию»);} оно ничего не меняет, там же используются классы и оператор throw, а этот код вызывает ошибку компилятора |
Seva |
|
Цитата forward89 @ 08.08.07, 12:39 оно ничего не меняет, там же используются классы и оператор throw, а этот код вызывает ошибку компилятора Это ты о чём? |
GarF1eld |
|
int i=0; int a=10; double b; try { b = a/i; } catch(EDivideByZero &ex) { //деление на 0 ShowMessage(ex.Message); } catсh(…) { ShowMessage(«Неизвестная ошибка»); } Добавлено 08.08.07, 12:47 |
Akme |
|
try{ int i=0; i=i/i; /* другой код */ }catch(EDivByZero &e) //ловим деление на 0 { ShowMessage(«Деление на ноль»); }catch(…) // ловим всё остальное { ShowMessage(«незапланированная ошибка»); } Добавлено 08.08.07, 12:48 |
sten_11 |
|
Senior Member Рейтинг (т): 13 |
Цитата оно ничего не меняет, там же используются классы и оператор throw, а этот код вызывает ошибку компилятора Разъясни ???? У меня поймал исключение, выкинул мессаг. |
forward89 |
|
Junior Рейтинг (т): нет |
Цитата int i=0; int a=10; double b; try { b = a/i; } catch(EDivideByZero &ex) { //деление на 0 ShowMessage(ex.Message); } catсh(…) { ShowMessage(«Неизвестная ошибка»); }
ошибка компиляции: На счет оператора throw читал по линке http://www.programmersclub.ru/39, что в обработке ошибок используется throw Добавлено 08.08.07, 14:16 catch(EDivideByZero &ex) а catch(EDivByZero &ex) тогда компилится, но ошибку не отлавлюет все равно, мессага от компилятора — обработка не срабатывает ( |
GarF1eld |
|
код писал от руки, так что ошибки могли быть. int a = 10; int b = 0; try { float c = a / b; } catch(EDivByZero &ex) { MessageBox(NULL, ex.Message.c_str(), «Division by zero», MB_OK | MB_ICONERROR); } Добавлено 08.08.07, 14:53 Добавлено 08.08.07, 14:55 |
forward89 |
|
Junior Рейтинг (т): нет |
ну да тогда все работает. Тогда если несколько усложнить задачу, скажем try { fmMain->server->Open(); } catch(…) { ShowMessage(«Can’t create server»); } Вот в этой комплекции открывается порт сервера, при ошибке выводится сообщение о невозможности создания, а в случае если все прошло без ошибок надо вывести месагу о Успешном создании. Как это сделать, если поместить строчку ШоуМессадж(«Все ок») после этого кода, тогда при ошибке будет выводится оба сообщения- неверно. А как же тогда организовать такую шнягу? И кто что может сказать о throw? Добавлено 08.08.07, 16:26 |
GarF1eld |
|
Цитата forward89 @ 08.08.07, 15:36 Как это сделать try { fmMain->server->Open(); // если происходит ошибка, то управление сразу передается в блок catch т.е код после этих коментов при ошибке не выполняется ShowMessage(«Server created succesfully»); } catch(…) { ShowMessage(«Can’t create server»); } Добавлено 08.08.07, 16:59 if (Edit1->Text.ToInt() < 1) throw EAbort(«Entered number is less than zero»); //генерирует исключение Добавлено 08.08.07, 17:03 class EMyException : public EAbort { public: EMyException(const AnsiString Msg): EAbort (Msg) {} }; //… throw EMyException(«message»); //… catch(EMyException &ex) { //… } Добавлено 08.08.07, 17:06 Цитата forward89 @ 08.08.07, 15:36 Использовать трай-кетч Лучше используй try…catch |
Seva |
|
При делении на нуль в Билдере по умолчанию используются два исключения: |
Akme |
|
class TMyExcept { }; // где-то в коде…. try{ i=i/i; /* Много операций с РАЗНЫМИ числами */ if( что_то_меня_не_устраивает ) throw TMyExcept(); ShowMessage(«Они таки поделились!»); } catch(EDivByZero &e) //ловим деление на 0 { ShowMessage(«Деление на ноль целых чисел»); } catch(EZeroDivide &e) { ShowMessage(«Деление на ноль вещественных чисел»); } catch( TMyExcept &e ) { ShowMessage(«Я таки поймал свой эксепшн!!!») } catch(…) // ловим всё остальное { ShowMessage(«незапланированная ошибка»); } Неужели еще что-то непонятно? |
Uhri |
|
forward89, при использовании throw обрати внимание на конструкторы и деструкторы. Если ты используешь throw в конструторе, то объект не будет создан в случае вызова исключения, например: class MyClass { public: MyClass() { throw(EMyError()); }; class EMyError {}; }; //где-то в коде MyClass * cls(NULL); try { cls = new MyClass; } catch(MyClass::EMyError) { if(!cls) ShowMessage(«Ты увидишь это сообщение, т.е. объект не создан!»); } Если в деструкторе, то нужно быть внимательным, и вызывать исключение, после освобождения всей памяти, например: class MyClass { int * pArr; public: MyClass() : pArr(NULL) { pArr = new int [10]; }; ~MyClass() { throw(MyError()); delete [] pArr; pArr = NULL; }; class EMyError {}; }; //где-то в коде MyClass * cls = new MyClass; //… чего-то делаем try { delete cls; } catch(MyClass::EMyError) { ShowMessage(«Исключение EMyError не позволит освободить память объекта pArr»); } Добавлено 09.08.07, 07:54 |
forward89 |
|
Junior Рейтинг (т): нет |
Это все понятно, спасибо огромное. но терь конкретная проблема: void TChat::ConnectToServer(AnsiString HOST_) { // функция подключения к серверу fmMain->client->Host = HOST_; try { fmMain->client->Open(); fmMain->txt_Chat->Lines->Add(«Подключенок к серверу:»+HOST_); } catch(…) { ShowMessage(«Сервер не отвечает»); } } где client — это обьект типа TClientSocket. И вот при попытке подключения ошибочного подключения выполняется строчка fmMain->txt_Chat->Lines->Add(«Подключенок к серверу:»+HOST_); и выводитя месага от компилятора «Asynchronus socket error 10049». Исключение не перехватывается!!! Почему? |
0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
0 пользователей:
- Предыдущая тема
- Borland C++ Builder/Turbo C++ Explorer
- Следующая тема
[ Script execution time: 0,0556 ] [ 16 queries used ] [ Generated: 8.06.23, 20:14 GMT ]