ВУЗ: Университет управления «ТИСБИ»
Категория: Учебное пособие
Дисциплина: Объектно-ориентированное программирование
Добавлен: 20.10.2018
Просмотров: 725
Скачиваний: 9
13. Исключения и их объектная обработка
Исключения (exception) – реакция вычислительной системы на
некоторую особую ситуацию, возникшую при работе приложения. Такие
особые ситуации связаны с возникновением в исполняемом коде программы
какой-либо ошибки (run–time error, ошибки времени выполнения),
например - обращение к несуществующему файлу, неправильная работа с
адресным указателем, несоответствие типов данных и т.д. В этом случае
программа не может дальше выполняться и аварийно завершается с выводом
некоторого кода ошибки.
Любая современная программа должна уметь перехватывать такие
ситуации и, по возможности, обрабатывать их тем или иным способом. Для
этого обычно в программу вставляется большое число проверок разных
условий, что делает логику программы достаточно запутанной. Механизм
исключений позволяет упростить обработку особых ситуаций, выделив в
программе специальные блоки - обработчики исключений.
Все основные современные операционные системы и языки
программирования
поддерживают
централизованную
обработку
исключений, которая строится на объектных принципах. Объектная
обработка особых ситуаций основана на двух основных моментах:
все типовые особые ситуации классифицированы и формализованы в
виде специальных стандартных классов базовых библиотек
при возникновении особой ситуации автоматически создается объект
соответствующего класса, который несет информацию о возникшей
проблеме
Стандартные классы исключений построены по иерархическому
принципу в полном соответствии с тезисом «от общего к частному». Корнем
иерархии является абстрактный класс Exception, от которого порождаются
дочерние подклассы для различных типов ошибочных ситуаций. Например,
платформа .NET Framework поддерживает около 100 стандартных классов,
некоторые из которых представлены на следующей схеме:
Object // корневой класс всей иерархии классов
Exception // корень подиерархии классов для исключений
SystemException // имеет около 70 непосредственных потомков
// ошибки арифметических операций
// арифметическое переполнение
// несоответствие типов для массива
// выход индекса за границы массива
// ошибки при преобразовании типов
// ошибки при выполнении операций ввода/вывода
//попытка чтения за концом входн. потока
// обращение к несуществующему файлу
// использование пустого указателя
// программе не хватает памяти
// переполнение стека вызовов подпрограмм
Аналогичный вид имеют подиерархии классов исключений для платформы
Java и пакета Delphi. Для сравнения можно привести несколько стандартных
классов исключений, входящих в базовую библиотеку классов Delphi:
EInOutError // ошибки при выполнении операций ввода/вывода
EIntError // ошибки целочисленной арифметики
EMathError // ошибки при обработке вещественных чисел
EConvertError // ошибки преобразования типов
EAccessViolation // ошибки неправильного использования памяти
Знание иерархии классов исключений и умение использования этого
механизма является очень важным аспектом разработки современного
надежного программного обеспечения, поскольку оно позволяет реализовать
один из основных принципов квалифицированной разработки: «Хорошая
программа никогда не должна завершаться аварийно».
Перейдем к вопросу использования механизма исключений в
объектных программах. Для этого, прежде всего, в исходном тексте
необходимо выделить те операторы, которые потенциально могут быть
источниками ошибок времени выполнения. Далеко не каждый оператор
программы является потенциально опасным, поэтому очень важно знать
источники возможных неприятностей. База знаний по этим источникам –
упомянутые выше стандартные классы исключений.
Потенциально опасные операторы могут идти последовательно друг за
другом, либо могут быть разбросаны по всему исходному коду. Главное –
найти такие операторы и изолировать их в тексте программы с помощью
специальных синтаксических конструкций. Начало потенциально опасного
фрагмента программы во всех объектных языках принято обозначать
директивой try. За этой директивой должен следовать потенциально
опасный фрагмент кода, за которым размещается специальный блок
обработчиков исключений. Начало этого блока обозначается директивой
catch в языках Java и C# и директивой except в языке Delphi Pascal. Общий
вид защищенного от ошибок времени выполнения кода выглядит следующим
образом:
try
{ опасные операторы }
catch
{ обработка исключения }
try
опасные операторы
except
обработчики исключений
end;
Такой защищенный код работает следующим образом:
если при его выполнении исключения не возникают, то блок
catch/except вообще не выполняется, и управление передается
операторам, следующим за этим блоком
если же в защищенном коде возникает исключение, то оставшиеся
операторы защищенного кода не выполняются, а управление сразу
передается на блок обработки.
Простейшая обработка особой ситуации может лишь включать вывод
информационного сообщения, но даже это позволяет не прерывать
аварийно выполнение программы, а информировать пользователя о
возникшей проблеме и дать ему возможность как-то прореагировать на эту
ситуацию.
Пример: организация защиты от особой ситуации «Попытка деления на
ноль». Фрагмент программы с одним защищенным оператором:
. . . . . . . . . . // предыдущие операторы
try // начало
{ z = x / y; } // опасный оператор
catch
{ Console.WriteLine(“Что-то не так ”);
} // конец
. . . . . . . . . . // следующие операторы
. . . . . . . . // предыдущие операторы
try // начало
z := x / y; // опасный оператор
except
ShowMessage(„Ошибка, однако‟)
end; // конец
. . . . . . . . . // следующие операторы
Еще раз подчеркнем, что если при делении особой ситуации не
возникнет, то обработчики не активизируются, никакие сообщения не будут
выведены и выполнение программы продолжится операторами за блоком
обработки.
На практике рекомендуется использовать везде, где это возможно более
конкретный разбор возникшей проблемы. Для этого при оформлении блока
обработки можно указывать тип обрабатываемого исключения, используя
для этого имя соответствующего класса. Это имя задается в круглых
скобках после директивы catch или как часть Delphi-конструкции вида
on ИмяКласса do оператор;
В качестве примера рассмотрим фрагмент программы, в цикле
выполняющей поэлементное деление двух массивов. Здесь опасной опять же
является операция деления. Защиту такого кода можно организовать по-
разному. Можно защитить весь цикл деления, а можно – отдельно каждую
операцию деления внутри цикла. Результаты работы таких программ будут
разными:
в первом случае выполнение цикла будет прервано при первой
попытке деления на ноль и остальные элементы массивов не будут
обработаны
во втором случае цикл всегда будет выполнен полностью, просто для
нулевых элементов массива-делителя будут зафиксированы особые
ситуации, они будут обработаны блоком обработки и это даст
возможность продолжить работу цикла.
Второй вариант интересен тем, что он демонстрирует один из основных
принципов механизма исключений, а именно: если исключение обработано
программой, соответствующий объект уничтожается, и программа может
продолжить
свое
выполнение.
Далее
приводятся
фрагменты
соответствующих программ (для краткости массивы имеют имена А и В).
// Вариант 1.
try { // защищенный фрагмент-цикл
for (int i=0; i<10; i++)
{ A[i] = A[i] / B[i]; }
}
catch (DivideByZeroException)
{ Console.WriteLine(“Делитель = 0”);
} // конец
. . . . . . . . . . // следующие операторы
// Вариант 1.
try // защищенный фрагмент-цикл
for i:= 1 to 10 do
A[i] := A[i] / B[i];
except on EDivByZero do
ShowMessage(„Делитель = 0‟)
end; // конец
. . . . . . . . // следующие операторы
// Вариант 2.
for (int i=0; i<10; i++) {
// Вариант 2.
for i:= 1 to 10 do