Глава 6. Подсистема динамического выделения памяти
Обработка исключений (exception handling) позволяет упорядочить обработку ошибок времени исполнения. Используя обработку исключений C++, программа может автоматически вызвать функцию-обработчик ошибок тогда, когда такая ошибка возникает. Принципиальным достоинством обработки исключений служит то, что она позволяет автоматизировать большую часть кода для обработки ошибок, для чего раньше требовалось ручное кодирование.
Обработка исключений в C++ использует три ключевых слова: try, catch и throw. Те инструкции программы, где ожидается возможность появления исключительных ситуаций, нужно помещать в блоке try. Если в блоке try возникает исключение, т. е. ошибка, то генерируется исключение. Исключение перехватывается в блоке catch и обрабатывается. Ниже это общее описание будет рассмотрено более подробно.
Инструкция, генерирующая исключение, должна исполняться внутри блока try. Вызванные из блока try функции также могут генерировать исключения. Всякое исключение должно быть перехвачено инструкцией catch, которая непосредственно следует за инструкцией try, сгенерировавшей исключение. Общая форма блоков try и catch показана ниже:
try
{
// блок try
}
catch(тип1 аргумент1)
{
// блок catch
}
catch(тип2 аргумент2)
{
// блок catch
}
catch(тип3 аргумент3)
{
// блок catch
}
catch(тип4 аргумент4)
{
// блок catch
}
Размеры блока try могут изменяться в больших пределах. Например, блок try может содержать несколько инструкций какой-либо функции, либо же, напротив, включать в себя весь код функции main(), так что вся программа будет охвачена обработкой исключений.
Когда исключение сгенерировано, оно перехватывается соответствующей инструкцией catch, обрабатывающей это исключение. Одному блоку try может отвечать несколько инструкций catch. Какая именно инструкция catch исполняется, зависит от типа исключения. Это означает, что если тип данных, указанных в инструкции catch, соответствует типу данных исключения, то только эта инструкция catch и будет исполнена. Когда исключение перехвачено, аргумент получает его значение. Перехваченным может быть любой тип данных, включая созданные программистом классы. Если никакого исключения не сгенерировано, то есть никакой ошибки не возникло в блоке try, то инструкции catch выполняться не будут.
Общая форма записи инструкции throw имеет вид:
throw исключение;
Инструкция throw должна выполняться либо внутри блока try, либо в функции, вызванной из, блока try. В записанном выше выражении исключение обозначает сгенерированное значение.
Если генерируется исключение, для которого отсутствует подходящая инструкция catch, может произойти аварийное завершение программы. При генерации необработанного исключения вызывается функция terminate(). По умолчанию terminate() вызывает функцию abort(), завершающую выполнение программы. Однако можно задать свою собственную обработку, используя функцию set_terminate(). Ниже представлен пример, иллюстрирующий способ обработки исключений в C++:
#include <iostream.h>
int main()
{
cout << "Start\n";
try // начало блока try
{
cout << "Inside try block\n";
throw 100; // генерация ошибки
cout << "This will not execute";
}
catch(int i) // перехват ошибки
{
cout << "Caught an exception -- value is: << i << "\n";
}
cout << "End";
return 0;
}
Программа выведет на экран следующий текст:
Start
Inside try block
Caught an exception -- value is: 100
End
Рассмотрим внимательнее эту программу. Как можно видеть, блок try содержит три инструкции. За ним следует инструкция catch(int i), обрабатывающая исключения целого типа. В блоке try будут выполняться только две инструкции: первая и вторая — throw. Как только исключение было сгенерировано, управление передается блоку catch, а блок try прекращает свое исполнение. Таким образом, инструкция после инструкции throw никогда не выполняется.
Обычно код в инструкции catch пытается исправить ошибку путем выполнения подходящих действий. Если ошибку удалось исправить, то выполнение продолжается с инструкции, непосредственно следующей за catch. Однако иногда не удается справиться с ошибкой и блок catch завершает программу путем вызова функции exit() или функции abort().
Рассмотрим еще один пример с исключением строкового типа:
#include <iostream.h>
int main()
{
char *buf;
try
{
buf = new char[512];
if(!buf) throw "Memory allocation failure!";
}
catch(char *string)
{
cout << "Caught exception: " << string << "\n";
}
// ...
return 0;
}
При генерации исключения оно перехватывается блоком catch, а строка "Memory allocation failure!" передается блоку catch в качестве параметра.