Файлы и потоки

ОБРАБОТКА ИСКЛЮЧЕНИЙ В БЛОКАХ 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 может производиться несколькими принципи-

ально различными (с точки зрения пользователя) способами:

• использование библиотечных компонентов

• работа с файлами как с потоками в стиле С

• работа с файлами как с потоками в стиле С++

Рассмотрим эти возможности.

2.10.1 Файловый ввод/вывод с помощью компонентов

Работа с текстовыми файлами может осуществляться с помощью методов

LoadFromFile и SaveToFile, имеющихся у классов TStrings и TStringList. Эти

Типы данных в языке C++ 155

классы описывают списки строк и обладают множеством методов, позволяющих

манипулировать строками.

Если вы хотите в своем приложении прочитать содержимое некоторого тексто-

вого файла, обработать текст и сохранить его в файле, вы можете сделать это сле-

дующим образом. Объявите и создайте две глобальные переменные: список типа

TStringList, в котором будет храниться текст файла, и строковую переменную

типа AnsiString, в которой можете сформировать имя файла. Например:

TStringList *List = new TStringList;

AnsiString SFile = "Test.txt";

He забудьте только, что если требуемый файл расположен не в текущем ката-

логе и вам надо указать путь к файлу, то обратные слэши в записи пути должны

быть сдвоенные (см. разд. 1.5.1). Например, если вам требуется файл "c:\My-

Test\Test.txt", то вы должны записать его как "с:\\MyTest\\Test.txt".

В момент, когда вы хотите загрузить в свой список файл, надо выполнить опе-

ратор

List->LoadFromFile(SFile);

Впрочем, ограничиться таким оператором можно, если есть уверенность, что

требуемый файл существует. В противном случае код надо несколько усложнить,

чтобы можно было перехватить сгенерированное исключение. Например:

try{

List->LoadFromFile(SFile);

}

catch(...) {

ShowMessage("Файл \"" + SFile +"\" не найден");

}

Если файл нормально загрузился в список List, вы можете работать с его тек-

стом. Текст расположен в свойстве списка Strings[int Index], в котором каждая

строка имеет тип AnsiString. Индексы начинаются с нуля. Для нашего примера

List->Strings[0] — это первая строка, List->Strings[l] — вторая и т.д.

Для списков типа TStringList предусмотрено множество методов. При обра-

ботке отдельных строк вы можете использовать операции и методы, предусмотрен-

ные для строк типа AnsiString (см. разд. 2.5.2 и соответствующие разделы гл. 3).

Если вы хотите сохранить файл после проведенного редактирования, можно

выполнить оператор

List->SaveToFile(SFile);

где SFile содержит прежнее или новое имя файла.

При открытии и сохранении файла вы можетевоспользоваться стандартными

диалогами Windows, вызываемыми через соответствующие компоненты С++Ви-

ilder.

Если вы открываете файл для того, чтобы пользователь мог его просмотреть,

что-то в нем отредактировать и сохранить, вы можете обойтись без описанного

выше объекта типа TStringList. Для этих целей проще воспользоваться много-

строчными окнами редактирования типов ТМето или TRichEdit. В последнем

случае вы можете работать не только с обычными текстовыми файлами, но и

с файлами в обогащенном формате RTF. Свойства Lines этих компонентов имеют

тип TStrings, что позволяет применять к ним непосредственно методы LoadFrom-

File и SaveToFile. Например:

Memol->Lines->LoadFromFile(SFile);

RichEditl->Lines->LoadFromFile(SFile);

Через компоненты C++Builder можно работать не только с текстовыми файла-

ми, но и с файлами изображений и мультимедиа.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: