Обработка исключений Исключения (exceptions) - это неожиданные ситуации, возникающие при выполнении программы Типичные ситуации это "деление на нуль",

Презентация:



Advertisements
Похожие презентации
Обработка исключительных ситуаций Исключительная ситуация (исключение) – это ошибка, возникающая во время выполнения программы. Например, ошибка работы.
Advertisements

Лекция 8 Область видимости Время жизни. Область видимости Область видимости – характеристика именованного объекта Область видимости - часть текста программы,
Практическое занятие 6. Функции. Большинство языков программирования используют понятия функции и процедуры. C++ формально не поддерживает понятие процедуры,
Преобразования типов В языке C/C++ имеется несколько операций преобразования типов. Они используются в случае, если переменная одного типа должна рассматриваться.
Основы информатики Классы Заикин Олег Сергеевич zaikin.all24.org
Программирование Часть 8 Обработка исключений.
Работа с файлами Сазонов Д.О. ПМиЭММ Часть 2. Тема занятия: Работа с файлами через потоки Для реализации файлового ввода/вывода, необходимо включить в.
Полиморфизм Полиморфизм (polymorphism) - последний из трех "китов", на которых держится объектно-ориентированное программирование Слово это можно перевести.
Основы информатики Лекция. Функции Заикин Олег Сергеевич
Обработка исключений Основы метапрограммированияОбработка исключений Основы метапрограммирования.
Потоки Язык C++ не обеспечивает средств для ввода/вывода Ему это и не нужно; такие средства легко и элегантно можно создать с помощью самого языка Традиционно.
Функции Функция – именованная последовательность описаний и операторов, выполняющая некоторое действие. Может иметь параметры и возвращать значение. Функция.
Занятие 2. Конструкции принятия решений. Конструкции принятия решений позволяют приложениям проверять условия и выбирать направление действий. Другим.
Переменные и операторы УРОК 2. Переменные ПЕРЕМЕННАЯ – ?... контейнер для хранения данных. Переменная имеет имя – это….? последовательность букв, цифр.
1 Обработка исключений в Java Одно из важнейших преимуществ Java – разработанный на уровне языка механизм обработки исключений. Исключение в Java - это.
24. Обработка исключений Основные вопросы Понятие исключительной ситуации. Проблемы при описании и обработке исключительных ситуаций обычными.
Инструкции C++ Условная инструкция Формат: if (условие) оператор; else оператор; Пример: if (i!=0) { if (j) j++; if(k) k++; else if(p) k--; } else i--;
ПРОЦЕДУРЫ И ФУНКЦИИ CPascal Подпрограмма – группа операторов реализующая законченный алгоритм и оформленная как самостоятельная синтаксическая единица.
С++, ООП Семинар 2 Рябова Анна Сергеевна
Министерство образования Республики Беларусь Белорусский государственный университет Управляющие структуры языков программирования.
Транксрипт:

Обработка исключений Исключения (exceptions) - это неожиданные ситуации, возникающие при выполнении программы Типичные ситуации это "деление на нуль", "достижение конца файла", "переполнение в арифметических операциях" и т.п. В языке Си++ практически любое состояние, достигнутое в процессе выполнения программы, можно заранее определить как особую ситуацию (исключение) и предусмотреть действия, которые нужно выполнить при ее возникновении Механизм обработки особых ситуации присутствовал в разных языках программирования до появления Си++. Один из примеров - язык ПЛ/1

Для реализации механизма обработки исключений в язык Си++ введены следующие три ключевых (служебных) слова: try (контролировать), catch (ловить), throw (генерировать, порождать, бросать, посылать, формировать) Служебное слово try позволяет выделить в любом месте исполняемого текста программы так называемый контролируемый блок: try { операторы } Среди операторов, заключенных в фигурные скобки могут быть: описания, определения, обычные операторы языка Си++ и специальные операторы генерации (порождения, формирования) исключений: throw выражение_генерации_исключения ;

Когда выполняется такой оператор, то с помощью выражения, использованного после служебного слова throw, формируется специальный объект, называемый исключением Исключение создается как статический объект, тип которого определяется типом значения выражение_генерации_исключения. После формирования исключения исполняемый оператор throw автоматически передает управление (и само исключение как объект) непосредственно за пределы контролируемого блока В этом месте (за закрывающейся фигурной скобкой) обязательно находятся один или несколько обработчиков исключений, каждый из которых идентифицируется служебным словом catch и имеет в общем случае следующий формат: catch (тип исключения имя){ операторы }

Об операторах в фигурных скобках здесь говорят как о блоке обработчика исключений. Обработчик исключений (процедура обработки исключений) внешне и по смыслу похож на определение функции с одним параметром, не возвращающей никакого значения Когда обработчиков несколько, они должны отличаться друг от друга типами исключений. Все это очень похоже на перегрузку функции, когда несколько одноименных функций отличаются спецификациями параметров Так как исключение передается как объект определенного типа, то именно этот тип позволяет выбрать из нескольких обработчиков соответствующий посланному исключению.

Классический алгоритм Евклида определения наибольшего общего делителя двух целых чисел (х, у) может применяться только при следующих условиях: оба числа х и у неотрицательные оба числа х и у отличны от нуля На каждом шаге алгоритма выполняются сравнения: если х == у, то ответ найден если х < у, то у заменяется значением у - х если х > у, то х заменяется значением х - у

Следующая программа содержит функцию GCM( ) для определения наибольшего общего делителя, включающую контролируемый блок с проверкой исходных данных В основной программе main( ) трижды вызывается функция GCM( ) Два последних вызова выполнены с неверными значениями параметров.

// Р12-01.СРР - GCM - Greatest Common Measure #include // Определение функции с генерацией, // контролем и обработкой исключений: int GCM(int x, int у) { // Контролируемый блок: try { if (x==0 || у==0) throw "\nZERO!"; if (х < 0) throw "\nNegative parameter 1."; if (y < 0) throw "\nNegative parameter 2."; while (х != y) { if (х > у) x = x - y; else у = у - x; } return x; } // Конец контролируемого блока try // Обработчик исключений стандартного типа "строка": catch (const char *report) { cerr

Служебное слово try определяет следующий за ним набор операторов в фигурных скобках как контролируемый блок Среди операторов этого контролируемого блока три условных оператора анализируют значения параметров При истинности проверяемого условия в каждом из них с помощью оператора генерации throw формируется исключение, т.е. создается объект - символьная строка, имеющая атрибуты const char * При выполнении любого из операторов throw естественная последовательность исполнения операторов функции прерывается и управление автоматически без каких-либо дополнительных указаний программиста передается обработчику исключений, помещенному непосредственно за контролируемым блоком (Это чуть-чуть похоже на оператор goto).

void main() { // Безошибочный вызов: cout

Так как в данной программе обработчик исключений локализован в теле функции, то ему доступны значения ее параметров (x,у). Поэтому при возникновении каждого исключения в поток вывода сообщений об ошибках сегг выводится символьная строка с информацией о характере ошибки (нулевые параметры или отрицательные значения параметров) и значениях параметров, приведшие к возникновению особой ситуации и к генерации исключения Здесь же в составном операторе обработчика исключений выполняется оператор return 0; Тем самым при ошибках возвращается необычное нулевое значение наибольшего общего делителя При естественном окончании выполнения функции GCM( ), когда становятся равными значения х и у, функция возвращает значение x. Обратите внимание, что исключения (const char *) одного типа посылаются в ответ на разные ситуации, возникающие в функции.

Хотя разобранная программа демонстрирует особенности механизма особых ситуаций (исключений), но в ней нет никаких преимуществ перед стандартными средствами анализа данных и возврата из функций. Как генерация исключений, так и их обработка выполняются в одной и той же функции, что, не типично для эффективного применения исключений Использование механизма обработки исключений полезнее в тех случаях, когда автор функции только констатирует наличие особых ситуаций и предлагает программисту (использующему функцию) самостоятельно решать вопрос о выборе правил обработки исключений в вызывающей программе.

В следующей программе используется другой вариант функции для определения наибольшего общего делителя (НОД) двух целых чисел, передаваемых в качестве аргументов В теле указанной функции GCM_NEW( ) нет контролируемого блока и обработчика исключений, но сохранены "генераторы" исключений Контролируемый блок и обработчик исключений перенесены в функцию main( ). Все вызовы функции (верный и с ошибками в параметрах) помещены в контролируемый блок.

// Р12-02.СРР - функция с генерацией, но без контроля исключений #include int GCM_NEW(int x, int у) // Определение функции { if (х == 0 || у == 0) throw "\nZERO!"; if (х < 0) throw "\nNegative parameter 1."; if (y < 0) throw "\nNegative parameter 2."; while (х != y) { if (х > у) x = x - y; else у = у - x; } return x; }

// Контроль и обработка исключений в вызывающей программе void main() { try // Контролируемый блок { cout

Как видно из результатов программа прекращает работу после обработки первого исключения. Так как обработка исключений выполняется вне тела функции, то в обработчике недоступны параметры функции и тем самым утрачивается возможность наблюдения за значениями приведшими к особой ситуации Это снижение информативности можно преодолеть, введя специальный тип для исключения

// P12-03.CPP #include struct DATA { int n,m; char *s; DATA(int x, int y, char *c) {n=x; m=y; s=c;} // Конструктор }; int GCM_ONE(int x, int y) { if (x ==0 ||| y == 0) throw DATA(x,y, "\nZERO"); if (x < 0) throw DATA(x,y, "\nNegative paramete 1."); if (y < 0) throw DATA(x,y, "\nNegative paramete 2."); while (x != y) { if (x > y) x = x-y; else y = y -x; } return x; }

void main() { try { cout

Отметим, что объект класса DATA формируется в теле функции при выполнении конструктора класса. Если бы этот объект не был исключением, он был бы локализован в теле функции и недоступен в точке ее вызова. Но по определению исключений, они создаются как временные статические объекты В данном примере исключения как безымянные объекты класса DATA формируются в теле функции, вызываемой из контролируемого блока. В блоке обработчика исключений безымянный объект типа DATA инициализирует переменную DATA d и тем самым информация из исключения становится доступной в теле обработчика исключений, что демонстрирует результат

Итак, чтобы исключение было достаточно информативным, оно должно быть объектом класса, причем класс обычно определяется специально. В приведенной программе класс для исключений определен как глобальный, т.е. он доступен как в функции GCM_ONE( ), где формируются исключения, так и в основной программе, где выполняется контроль за ними и, при необходимости, их обработка Внешне исключение выглядит как локальный объект той функции, где оно формируется. Однако исключение не локализуется в блоке, где использован оператор его генерации. Исключение как объект возникает в точке генерации, распознается в контролируемом блоке и передается в обработчик исключений. Только после обработки оно может исчезнуть. Нет необходимости в глобальном определении класса объектов-исключений. Основное требование к нему - известность в точке формирования (throw) и в точке обработки (catch).

Следующий пример иллюстрирует сказанное Класс (структура) DATA определен отдельно как внутри функции GCM_TWO( ), так и в основной программе. Никаких утверждений относительно адекватности этих определении явно не делается. Но передача исключений проходит вполне корректно, что демонстрируют результаты.

//Р12-04.СРР - локализация определений типом (классов) исключений #include int GCM ONE(int x, int y) { struct DATA // Определение типа локализовано в функции { int n, m; char *a; DATA(int x, int у, char *c) // Конструктор класса { n = х; в = у; s " с; } }; if (х == 0 || у == 0) throw DATA(x,y,"\nZERO"); if (х < 0) throw DATA(x,y,"\nNegative parameter 1."); if (y < 0) throw DATA(x,y,"\nNegative parameter 2."); while (х != y) { if (х > у) x = x - y; else y = y - x; } return x; }

void main() { struct DATA // Определение типа локализовано в main() { int n, m; char * a; DATA(int x, int y, char *c) // Конструктор { n = x; m = y; s = с;) }; try { cout

Результат выполнения программы: GCM_ONE(66,44) = 22 Negative parameter 1. x = -12, у = 8

Синтаксис и семантика генерации и обработки исключений Общая схема размещения указанных блоков: try { операторы контролируемого блока } catch (спецификация исключения) { операторы обработчика исключений }... catch (спецификация исключения) { операторы обработчика исключений }

В приведенных выше программах использовалось по одному обработчику исключений. Это объясняется "однотипностью" формируемых исключений (только типа const char * или только типа DATA) В общем случае в контролируемом блоке могут формироваться исключения разных типов и обработчиков может быть несколько Размещаются они подряд, последовательно друг за другом и каждый обработчик "настроен" на исключение конкретного типа

Спецификация исключения, размещенная в скобках после служебного слова catch, имеет три формы: catch (тип имя) {... } catch (тип) {... } catch (... ) {... ) Первый вариант подобен спецификации формального параметра в определении функции. Имя этого параметра используется в оператоpax обработки исключения. С его помощью к ним передается информация из обрабатываемого исключения. Второй вариант не предполагает использования значения исключения. Для обработчика важен только его тип и факт его получения. В третьем случае (многоточие) обработчик реагирует на любое исключение независимо от его типа. Так как сравнение "посланного" исключения со спецификациями обработчиков выполняется последовательно, то обработчик с многоточием в качестве спецификации следует помещать только в конце списка обработчиков

//Р12-05.СРР - исключения без передачи информации #include class ZeroDivide { }; // Класс без компонентов class Overflow { }; // Класс без компонентов // Определение функции с генерацией исключений: float div(float n, float d) { if (d == 0.0) throw ZeroDivide(); // Вызов конструктора double b = n/d; if (b > 1e+30) throw OverflowQ; // Вызов конструктора return b; }

// Глобальные переменные float x = 1e-20, у = 5.5, z = 1e+20, w = 0.0; // Вызывающая функция с выявлением и обработкой исключений: void RR(void) { try { у = div(4.4,w); z = div(z,x) ; } // Контролируемый блок: // Последовательность обработчиков исключений: catch (Overflow) { cerr

void main() { PR(); // Вызов функции div() с нулевым делителем w: RR(); // Вызов функции div() с арифметическим переполнением: cout

Если при выполнении операторов контролируемого блока исключении не возникло, то ни один из обработчиков исключений не используется, и управление передается в точку непосредственно после них Если в контролируемом блоке формируется исключение, то делается попытка найти среди последующих обработчиков соответствующий исключению обработчик и передать ему управление. После обработки исключения управление передается в точку окончания последовательности обработчиков. Возврата в контролируемый блок не происходит Если исключение создано, однако соответствующий ему блок обработки отсутствует, то автоматически вызывается специальная библиотечная функция terminate( ). Выполнение функции terminate( ) завершает выполнение программы.

Генерация исключений Выражение, формирующее исключение, может иметь две формы: throw выражание_генерации_исключения ; throw; Первая из указанных форм уже продемонстрирована в приведенных программах. Важно отметить, что исключение в ней формируется как статический объект, значение которого определяется выражением генерации Несмотря на то, что исключение формируется внутри функции как локальный объект, копия этого объекта передается за пределы контролируемого блока и инциализирует переменную, использованную в спецификации исключения обработчика. Копия объекта, сформированного при генерации исключения, существует, пока исключение не будет полностью обработано

В некоторых случаях используется вложение контролируемых блоков, и не всегда исключение, возникшее в самом внутреннем контролируемом блоке, может быть сразу же правильно обработано. В этом случае в обработчике можно использовать сокращенную форму оператора: throw ; Этот оператор, не содержащий выражения после служебного слова, "ретранслирует" уже существующее исключение, т.е. передает его из процедуры обработки и из контролируемого блока, в который входит эта процедура, в процедуру обработки следующего (более высокого) уровня. Естественно, что ретрансляция возможна только для уже созданного исключения. Поэтому оператор throw; может использоваться только внутри процедуры обработки исключений и разумен только при вложении контролируемых блоков.

В качестве иллюстрации сказанного приведем следующую программу с функцией compare( ), анализирующей четность (или нечетность) значения целого параметра Для четного (even) значения параметра функция формирует исключение типа const char *. Для нечетного (odd) значения создается исключение типа int, равное значению параметра В вызывающей функции GGO - два вложенных контролируемых блока. Во внутреннем - два обработчика исключений. Обработчик catch (int n), приняв исключение, выводит в поток cout сообщение и ретранслирует исключение, т.е. передает его во внешний контролируемый блок

//Р12-06.СРР - вложение контролируемых блоков // и ретрансляция исключений #include void compare(int k) // Функция, генерирующая исключения { if (k%2 != 0) throw k; // Нечетное значение (odd) else throw "even"; // Четное значение (even) }

void GG(int j) // Функция с контролем и обработкой исключений: { try { compare(j); } // Вложенный контролируемый блок catch (int n) { cout

void main() { GG(4); GG(7) ; } Результат работы программы: Even Odd Result = 7

В основной программе функция GGO вызывается дважды - с четным и нечетным параметрами Для четного параметра 4 функция после печати сообщения "Even" завершается без выхода из внутреннего контролируемого блока Для нечетного параметра выполняются две процедуры обработки исключений из двух вложенных контролируемых блоков. Первая из них печатает сообщение "Odd" и ретранслирует исключение. Вторая печатает значение нечетного параметра, снабдив его пояснительным текстом: "Result= 7"

Если оператор throw использовать вне контролируемого блока, вызывается специальная функция terminate( ), завершающая выполнение программы При вложении контролируемых блоков исключение, возникшее во внутреннем блоке, последовательно "просматривает" обработчики, пока не будет найдена подходящая функция обработки. Если во всей совокупности обработчиков не будет найден подходящий, то выполняется аварийное завершение программы с выдачей, например, такого сообщения: "Program Aborted". Аналогичная ситуация может возникнуть и при ретрансляции исключения, когда во внешних контролируемых блоках не окажется соответствующей исключению процедуры обработки

Особенно удобна обработка исключений при использовании классов Дело в том, что если обработчика данного типа исключения нет в операторах catch, приводимых за блоком try, то компилятор организует выход из данной функции и возврат в вызывающую функцию, при этом тип выброшенного исключения сохраняется, и осуществляется поиск catch c соответствующим типом исключения на более высоком уровне Поднимаясь по стеку вызовов функций в поисках нужного catch, компилятор сам вызывает деструкторы всех локальных объектов, тем самым обеспечивая нормальное освобождение памяти и дальнейшую работу программы

Для исключений удобно использовать иерархию классов, когда есть много типов исключений. В обработчиках исключений удобно использовать виртуальные функции. Например, рассмотрим функцию для записи информации на гибкий диск. Здесь возможно появление множества ошибок, таких как неформатированный диск, диск защищен от записи, пустой дисковод и т.д. Иерархия ошибок может быть реализована в виде классов:

class DiskError { public: virtual char * ErrorMessage() { return "Unknown disk error";} }; class DiskWriteError: public DiskError { public: char * ErrorMessage() { return "Disk write error";} }; class DiskWriteProtectedError: public DiskError { public: char * ErrorMessage() { return "Disk error: disk is protected";} };

void WriteDisk() { try { // здесь выполняется проверка доступа к диску и // выбрасывается одно из исключений // throw new DiskWriteError; или throw new DiskWriteProtectedError; } catch ( DiskError & err) { cout