Тема: Исключительные ситуации. Механизм обработки исключений

ПЛАН ЗАНЯТИЯ №31

Дисциплина: ОП.05 Основы программирования

Преподаватель: Машарова Р.В.

Курс: 3

Группа: 1 ПКС-20

Специальность: Программирование в компьютерных системах

Дата: 19.12.2022

Время проведения: 08.10-09.40, 1 пара

Тема: Исключительные ситуации. Механизм обработки исключений

Цель занятия:

Дидактическая: познакомиться с механизмом обработки исключений

Развивающая: развивать логическое и критическое мышление, умение обобщать и синтезировать знания

Вид занятия лекция

Литература:

1. Павловская Т.А. С/С++. Программирование на языке высокого уровня. 2 изд. – Спб. Питер, 2011. – 464 с.: ил.

2. Павловская Т. А., Щупак Ю. А. C/C++. Структурное и объектно-ориентированное программирование. Практикум. – Спб. Питер, 2010. – 352 с.

3. Семакин И.Г., Шестаков А.П. Основы алгоритмизации и программирования: учебник для студентов учреждений сред.проф. образования – М.: издательский центр «Академия», 2013 – 400 с.

4. Семакин И.Г., Шестаков А.П. Основы алгоритмизации и программирования. Практикум: учебник для студентов учреждений сред.проф. образования – М.: издательский центр «Академия», 2013 – 400 с.

Лекция №60

Тема: Исключительные ситуации. Механизм обработки исключений

1. Механизм обработки исключений

 

При работе с исключениями используются три ключевых слова try, catch и throw. При этом первые два обозначают так называемый блок try-сatch и связаны с обработкой возникших исключений, а throw – с выбрасыванием исключений. Синтаксически блок try-сatch выглядит следующим образом:

try {

<операторы>}

catch(<описание исключения>) {

<операторы обработки исключения>}

catch(<описание исключения>) {

<операторы обработки исключения>} …

Здесь <операторы> в блоке try представляют собой команды, во время выполнения

которых может произойти исключительная ситуация. В том случае если некоторый набор операторов заключен в блок try, говорят, что операторы находятся в защищенном блоке.

<описание исключения> показывает тип исключения, которое будет обрабатываться в блоке catch. Здесь может быть представлен любой тип данных, как базовый, так и производный. Кроме того здесь может быть описана переменная указанного типа, которую в дальнейшем можно будет использовать при обработке исключения. Помимо описания типа или переменной здесь может стоять троеточие, которое показывает, что данный блок catch обрабатывает исключения любого типа. <Операторы обработки исключения> в блоке catch предназначены для обслуживания возникшей исключительной ситуации определенного типа.

Однако в том случае, если при обработке исключения обнаруживается ошибка, или полностью нейтрализовать возникшую ситуацию не представляется возможным, блок catch сам может быть источником исключений, которые должны отлавливаться и обрабатываться в каком-либо внешнем блоке try-catch.Для генерации исключения внутри блока try должен быть выполнен оператор throw,параметром которого является объект-исключение желаемого типа.

throw<выражение>

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

Если соответствующий обработчик найден и его формальный параметр указан, как параметр, передаваемый по значению (catch (<тип><параметр>)), то параметр инициализируется копией объекта-исключения. В том случае, если параметр указывается по ссылке (catch (<тип>&<параметр>)), ссылка инициализируется адресом объекта- исключения.

После инициализации параметра запускается процесс так называемой раскрутки стека. В этом процессе выполняется уничтожение (вызываются деструкторы) всех объектов, созданных внутри блока try и находящихся в автоматической памяти. При этом уничтожение объектов производится в порядке обратном их созданию. После раскрутки стека выполняются <операторы обработки исключения>, располагающиеся в соответствующем блоке catch. В том случае, если при их выполнении никаких исключений не происходит, выполнение продолжается с оператора, следующего за последним из блоков try.

В качестве примера рассмотрим функцию вычисления скорости

doublevelocity( doubledistance, doubletime )

{if( distance< 0 ) throw “ошибка в расстоянии”; if ( time<= 0 ) throw 1;

returndistance / time;}

Здесь функция проверяет значения переданных ей аргументов и выбрасывает исключения разных типов (char* и int), если аргументы некорректны. Фрагмент кода, вызывающего такую функцию может выглядеть следующим образом.

voidtest(){

try {

double d, t, v; cout<<”Введите расстояние:”; cin>>d;

cout<<”Введите время:”;

cin>>t;

v = velocity(d, t); cout<<”скорость: “ << v;}

catch (char* msg) { cout>>msg;}

catch (int) {cout>>”ошибка в параметре ‘время’”;}}

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

classEInvalidArg{ protected:

charm_name[20]; public:

EInvalidArg(constchar *Name) { strncpy(m_name, Name, 20);

m_name[19] = 0;}

voidPrint() { printf( "Параметр <%s> имеет недопустимое " "значение", m_name ); }};

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

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

classEMyException{ protected:

char* m_msg; public:

EMyException( constchar *Msg )

{ m_msg = strdup(Msg); } EMyException( constEMyException&src )

{ m_msg = strdup(src.m_msg); }

~EMyException()

{ free(m_msg); }

const char* Message() { return m_msg; } virtual void Print() { puts( m_msg ); }};

classEInvalidArg: publicEMyException{ protected:

char *m_name; public:

EInvalidArg( constchar *Name ): EMyException("Недопустимый параметр")

{ m_name = strdup(Name); } EInvalidArg( constEInvalidArg&src ):

EMyException(src)

{ m_name = strdup(src.m_name); }

~EInvalidArg ()

{ free(m_name); } virtualvoidPrint()

{printf( "Недопустимый параметр <%s>", m_name ); }};

classEInvalidIndex: publicEMyException{ protected:

intm_min, m_max, m_ind; public:

EInvalidIndex( intMin, intMax, intInd ): EMyException("Неверный индекс")

{m_min=Min; m_max=Max; m_ind=Ind; } EInvalidIndex( constEInvalidIndex&src ):

EMyException(src)

{m_min=src.m_min; m_max=src.m_max; m_ind=src.m_ind;} virtualvoidPrint()

{printf( "Значение индекса (%d) выходит за диапазон”

“ [%d;%d]", m_ind, m_min, m_max );}};

Напомним, что при работе с исключениями создаются копии объектов-исключений.

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

classCVector{ private:

double *m_pData; int m_iSize;

public:

CVectoroperator +( constCVector&v2) {

if (m_iSize != v2.m_iSize) throwEInvalidArg(“v2”); CVectorres(m_iSize);

for( int i = 0; i <m_iSize; i++) res.m_pData[i] = m_pData[i]+v2.m_pData[i];

returnres;

}

double&operator []( intind ) { if (ind< 0 || ind>= m_iSize)

throwEInvalidIndex(0,m_iSize-1,ind); returnm_pData[ind];

}};

Фрагмент кода работы с реализованными выше операциями приведен ниже.

try {

CVector v1(3,3.3), v2(2,2.2), v3;

if (exc_no==1)v1[3] = 1.1; else if (exc_no==2)v3=v1+v2;elseputs(”Исключенийнебудет”);}

catch (constEMyException&e)

{ e.Print(); }catch ( … )

{ puts( "Неизвестное исключение" ); }

Здесь в зависимости от значения целочисленной переменной exc_no будет создана исключительная ситуация, соответствующая одному из введенных выше типов. Обратите внимание, что обработчик исключений здесь принимает параметр не по значению, а по ссылке, что гарантирует появление правильного сообщения при вызове виртуального метода Print (будут выдаваться сообщения «Значение индекса (3) выходит за диапазон [0;2]» и

«Недопустимый параметр <v2>»). Если бы параметр обработчика был указан по значению (catch (EMyException e)), то вызывался бы конструктор копирования для класса EMyException, который создавал бы объект базового класса, и мы бы увидели сообщения без важных деталей («Недопустимый параметр» и «Неверный индекс»).Следует отметить, что, так как при поиске обработчиков они просматриваются в том порядке, в котором записаны, и обработчик считается найденным, если объект-исключение является производным от типа, указанного в обработчике, то типы в обработчике исключений следует располагать в порядке расширения типа. При этом обработчик catch(…), который будет ловить все исключения, следует располагать последним. В ряде случаев обработчик не в состоянии до конца обработать возникшую ошибку. В этом случае он может выбросить новое исключение или повторно сгенерировать то же самое исключение путем вызова throw без параметров, передавая, таким образом, исключение во внешний блок try-catch.

 

Контрольные вопросы

1. Механизм обработки исключений