Обработка исключительных ситуаций

Содержание

Слайд 2

Сложность задачи обработки ошибочных ситуаций Концепция программирования с защитой от ошибок

Сложность задачи обработки ошибочных ситуаций

Концепция программирования с защитой от ошибок
Что

считать ошибкой?
«Полицейский» принцип обработки ошибочных ситуаций
Возврат признака завершения
«Регистрация» ошибок (значение глобальной переменной errno в С)
Обработчики ошибочных ситуаций
Слайд 3

Проблема обработки ошибок Главная проблема обработки исключительных ситуаций состоит в том,

Проблема обработки ошибок

Главная проблема обработки исключительных ситуаций состоит в том, что


разработчику библиотеки классов просто обнаружить возникновение ошибки при выполнении программы, но сложно принять решение, как с ней поступить.
пользователь знает, что предпринять в случае ошибки, но не имеет возможности ее обнаружить (иначе ошибка была бы обработана в пользовательском коде, и от библиотеки ничего бы не потребовалось)
Слайд 4

Идея обработки исключений в С++ Пространственное разделение события возникновения исключения и

Идея обработки исключений в С++

Пространственное разделение события возникновения исключения и его

обработки
Понятие исключения, как объекта и обработчика исключения, как функционального блока, принимающего аргумент
Механизм поиска обработчика путем разматывания стека
Предопределенная обработка исключений
Слайд 5

Пример заявления исключения типа const char* class String { public: //

Пример заявления исключения типа const char*

class String {
public:
// ………………………
char& operator[ ] (

int ){
if( i < 0 || length() < i )
throw "Index out of range.";
return text[i];
};
// ………………………
};
Слайд 6

Использование кода прячем в try-блок void foo( String& s ) {

Использование кода прячем в try-блок

void foo( String& s ) {
// .

. .
try {
do_something ( s );
}
catch ( const char* s ) {
// здесь пользователь имеет возможность
// обработать исключительную ситуацию
// например, так
cerr << “Произошла ошибка: ” << s << “\n”;
exit(1);
}
// Здесь продолжается выполнение при нормальном
// завершении функции do_something()
}
Слайд 7

Синтаксические элементы и правила Слова throw, try и catch являются ключевыми

Синтаксические элементы и правила

Слова throw, try и catch являются ключевыми словами языка

С++
Конструкция catch ( . . . . . ) { . . . . . }
называется обработчиком исключения и должна размещаться сразу за try-блоком, либо за другим обработчиком.
В круглых скобках содержится имя типа и, возможно, имя аргумента
Аргумент может использоваться подобно аргументу функции и передавать обработчику дополнительную информацию, связанную с исключением – значение исключения.
Слайд 8

Раскручивание стека вызовов Процесс генерации и последующей обработки исключений требует поиска

Раскручивание стека вызовов

Процесс генерации и последующей обработки исключений требует поиска соответствующего

обработчика от точки возникновения исключения через последовательность стековых фреймов вызовов функций до фрейма функции, имеющей искомый обработчик. Говорят, что происходит раскручивание стека вызовов.
Слайд 9

Изменение стека вызовов – I void func2( ) { int i=256;

Изменение стека вызовов – I
void func2( ) {
int i=256;
}
void func1(

) {
int k=128;
func2();
}
void main( void ){
int j=64;
func1();
}

младшие адреса памяти
старшие адреса памяти

стек

Код
Статические данные
Куча

вершина стека

j=64

Слайд 10

Изменение стека вызовов – II void func2( ) { int i=256;

Изменение стека вызовов – II
void func2( ) {
int i=256;
}
void func1(

) {
int k=128;
func2();
}
void main( void ){
int j=64;
func1();
}

младшие адреса памяти
старшие адреса памяти

стек

Код
Статические данные
Куча

вершина стека

j=64

адрес возврата в main()

k=128

фрейм для func1()

Слайд 11

Изменение стека вызовов – III void func2( ) { int i=256;

Изменение стека вызовов – III
void func2( ) {
int i=256;
}
void func1( )

{
int k=128;
func2();
}
void main( void ){
int j=64;
func1();
}

младшие адреса памяти
старшие адреса памяти

стек

Код
Статические данные
Куча

вершина стека

j=64

адрес возврата в main()

k=128

фрейм для func1()

адрес возврата в func1()

фрейм для func2()

i=256

Слайд 12

Механизмы раскручивания программного стека Естественное, в процессе выполнения программы; Связанное с

Механизмы раскручивания программного стека

Естественное, в процессе выполнения программы;
Связанное с обработкой исключительных

ситуаций;
Путем применения функции longjmp();
Путем прямой модификации регистра указателя стека CPU
Слайд 13

Обрабатываются только исключения, явно генерируемые некоторой функцией; Поддерживается окончательная модель обработки

Обрабатываются только исключения, явно генерируемые некоторой функцией;
Поддерживается окончательная модель обработки (т.е.

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

Особенности обработки исключений в языке С++

Слайд 14

Если заявлено исключение, для которого нет обработчика в цепочке вызовов, программа

Если заявлено исключение, для которого нет обработчика в цепочке вызовов, программа

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

Если после заявления исключения управление передано catch-блоку, то вне зависимости от

Если после заявления исключения управление передано catch-блоку, то вне зависимости от

результата последующих действий исключение считается обработанным;
Обработчик, как и обычная функция, может заявить исключение. Более того, в нем может использоваться оператор throw без параметра, что означает повторное генерирование исключения, обрабатываемого в данный момент;
Слайд 16

Обработка исключений и традиционные способы защиты «Полицейский» подход можно быть и

Обработка исключений и традиционные способы защиты

«Полицейский» подход
можно быть и поумнее
соответствует

случаю отсутствия обработчика
Возврат значения-сигнала ошибки
не всегда возможно (например, любой символ для операции индексации строки допустим)
проверка утомительна, удлиняет и загромождает код
Слайд 17

Нормальное завершение с установкой внешнего признака ошибки (errno) ошибка м.б. незамечена

Нормальное завершение с установкой внешнего признака ошибки (errno)
ошибка м.б. незамечена
сколько признаков

надо иметь?
Вызвать функцию-обработчик в случае ошибки (error handler function)
близок к механизму обработки исключений
нет единого стандарта
проблема программирования функций-обработчиков «по умолчанию»
Слайд 18

Что такое исключение? Исключение выступает одновременно как переменная и как тип

Что такое исключение?

Исключение выступает одновременно как переменная и как тип

данных (или как объект и как класс)
Оператор throw выбрасывает объект, а catch-обработчик ловит класс (или throw выбрасывает переменную, а catch ловит ее тип)
Слайд 19

Генерирование исключений При генерации исключения (throw) функции библиотеки исполняющей системы осуществляют

Генерирование исключений

При генерации исключения (throw) функции библиотеки исполняющей системы осуществляют

следующие действия
Создается и запоминается копия объекта (переменной). Т.е. если в точке генерации исключения оказывается недоступен копирующий конструктор (например, он не является public, а исключение заявляет не функция-друг), то возникает сообщение об ошибке.
Разматывается стек, с вызовом деструкторов локальных объектов, выходящих из области видимости
Слайд 20

Распознавание исключений Управление передается ближайшему catch-обработчику, совместимому с типом выброшенного исключения

Распознавание исключений

Управление передается ближайшему catch-обработчику, совместимому с типом выброшенного исключения
При

этом копия объекта-исключения передается, если это предусмотрено, обработчику в качестве параметра.
Слайд 21

Обработка исключений Обработчик считается найденным, а исключение обработанным, если: тип исключения

Обработка исключений

Обработчик считается найденным, а исключение обработанным, если:
тип исключения

соответствует типу, ожидаемому в обработчике. Переменной (объекту) типа T соответствует обработчик, перехватывающий T, const T, T&, const T&
тип обработчика является публичным базовым классом для заявленного исключения
обработчик ожидает указатель, и исключение является указателем, который может быть преобразован к типу обработчика по стандартным правилам преобразования указателей
встретился обработчик по умолчанию
Слайд 22

Обработчик по умолчанию Обработчик по умолчанию вызывается для исключения любого типа

Обработчик по умолчанию

Обработчик по умолчанию вызывается для исключения любого типа и

имеет вид
catch( ... ) {
// тело обработчика
}
Обработчик по умолчанию должен располагаться последним среди обработчиков данного try-блока.
Слайд 23

Пример Пусть класс Vector должен обнаруживать ошибки индексации и выделения памяти

Пример

Пусть класс Vector должен обнаруживать ошибки индексации и выделения памяти


class

Vector {
public:
class Range{};
class Memory{};
// . . . . .
Vector(int);
int& operator[ ](int);
// . . . . .
private:
int lbound, ubound;
int *v;
};
Слайд 24

Пример (продолжение) Vector::Vector (int size) { if( !(v=new int[size]) ) throw

Пример (продолжение)

Vector::Vector (int size) {
if( !(v=new int[size]) ) throw Memory();
else {

lbound = 0; ubound = size – 1; }
}
int& Vector::operator[](int i) {
if( i < lbound || ubound < i ) throw Range();
else return v[i – lbound];
}
Слайд 25

Пример (продолжение) Пользователь класса Vector может различить два исключения, включив два

Пример (продолжение)

Пользователь класса Vector может различить два исключения, включив два обработчика:

void f(void) {
try{
use_vectors();
}
catch( Vector::Range ){
// тело обработчика ошибки индексации
}
catch( Vector::Memory ){
// тело обработчика ошибки выделения памяти
}
}
Слайд 26

Повторное заявление исключений Исключение считается обработанным сразу после входа в обработчик.

Повторное заявление исключений

Исключение считается обработанным сразу после входа в обработчик. Поэтому
try{
do_something();
}
catch(

ExeptionType ){
// . . . . .
throw ExeptionType();
}
не вызовет бесконечного цикла.
Слайд 27

Вложенные обработчики try { // . . . . . }

Вложенные обработчики

try {
// . . . . .
}
catch( type )

{
try {
// код, в котором возможно
// возникновение ситуации типа type
}
catch( type ) {
// . . . . .
}
}

не вызовет бесконечного цикла

Слайд 28

Тип и значение исключений При выходе индекса за границы можно передать

Тип и значение исключений

При выходе индекса за границы можно передать ошибочное

значение


class Vector {
public:
class Range{
public:
int index;
Range( int i ) : index( i ) { };
};
// . . . . .
int& operator[ ](int);
// . . . . .
private:
int lbound, ubound;
int *v;
};

Слайд 29

int& Vector::operator[ ](int i) { if( i else return v[i-lbound]; }

int& Vector::operator[ ](int i) {
if( i < lbound || ubound <

i ) throw Range(i);
else return v[i-lbound];
}
void f( Vector& v ) {
// . . . . .
try{
use_vectors(v);
}
catch( Vector::Range r ){
cerr << “Bad index: “ << r.index << ‘\n’;
// . . . . .
}
// . . . . .
};
Слайд 30

Группировка исключений enum MathError { Overflow, Underflow, Zerodivide, /*. . .

Группировка исключений

enum MathError { Overflow, Underflow, Zerodivide, /*. . .

*/ }; // . . . . .
try {
// . . . . .
MathError m;
throw m;
}
catch( MathError m ) {
switch (m) {
case Overflow : /* . . . . . */ break;
case Underflow : /* . . . . . */ break;
case Zerodivide : /* . . . . . */ break;
default : /* . . . . . */;
}
}
Слайд 31

Иерархия исключений Объектно-ориентированный подход дает лучшее решение class MathError { };

Иерархия исключений

Объектно-ориентированный подход дает лучшее решение

class MathError { };
class Overflow :

public MathError { };
class Underflow : public MathError { };
class Zerodivide : public MathError { };
// . . . . .
Слайд 32

Далее… try { // . . . . . } catch(

Далее…

try {
// . . . . .
}
catch( Overflow ) {
//

обработка Overflow и его потомков
}
catch( MathError ) {
// обработка любого MathError,
// кроме Overflow и его потомков
}