Исключительная ситуация (исключение, exception) означает ошибку, возникшую во время выполнения программы. C++ предусматривает возможность обработки таких ошибок в самой программе используя обработчик исключений.
Обработка исключений в C++ осуществляется с помощью блоков try / catch. Основой обработки является программный блок try («попытаться»). В него помещается код, который потенциально может вызвать ситуацию, на которую программа не рассчитана: попытка чтения из несуществующего файла, недостаток свободной памяти и т.д. Если в контролируемом try-блоке или в функции, которая из него вызывается, возникает исключение, то оно перехватывается инструкцией catch («поймать»), которая расположена сразу после try-блока. Общий синтаксис блоков try/catch выглядит следующим образом:
try {
// любые инструкции, которые могут сгенерировать исключение
}
catch (type1 arg) {
// обработка исключения типа type1
}
catch (type2 arg) {
// обработка исключения типа type2
}
...
catch (typeN arg) {
// обработка исключения типа typeN
}
catch (...) {
// обработка исключений любых типов
}
В блоке try находятся код, который может породить исключительную ситуацию, не предусмотренную программой. Здесь может находиться как единственная инструкция, так и всё содержимое функции main, т.е. по сути – вся программа.
После блока try должен следовать хотя бы один блок catch. Если в процессе выполнения кода в try-блоке возникает исключение, то управление передаётся на тот блок catch, который предназначен для обработки исключений такого типа. Другие сatch-блоки, если они есть, игнорируются.
Когда управление передаётся в блок catch, его аргумент инициализируется значением исключения. После выполнения инструкций из блока catch управление передаётся первой инструкции, следующей за последним catch-блоком.
Если ни один из блоков catch не подходит для обработки выброшенного исключения, программа аварийно завершается вызовом функции terminate().
Для того чтобы перехватить исключение, его нужно предварительно выбросить (сгенерировать). Для этого используется оператор throw.
throw exception;
Здесь exception означает значение, которое будет выброшено в виде исключения. При его перехвате это значение присвоится аргументу catch-блока. Пример работы с исключением:
#include <iostream>
using namespace std;
void main()
{
setlocale(LC_ALL, "Russian");
try {
cout << "Блок try \n";
throw 12;
cout << "Никогда мы не увидим это сообщение\n";
}
catch (int par) {
cerr << "Перехватили исключение с номером " << par << endl;
}
catch (const char* str) {
cerr << "Перехватили исключение с ошибкой \n\t\""
<< str << '"' << endl;
}
cout << "Конец программы\n";
system("pause");
}
Результатом выполнения программы будет следующий вывод:
Блок try
Перехватили исключение с номером 12
Конец программы
Поскольку в программе выбросили исключение целого типа, его перехватил блок catch (int par). Он вывел свое сообщение и передал управление первой инструкции после блока try/catch. Это вывод сообщения о конце программе. Второй блок catch при этом не выполняется. Он может пригодиться, если в try-блоке будет выброшено исключение строкового типа, например:
throw "Текстовое описание ошибки";
В блоке try может быть выброшено исключение неизвестного (не предусмотренного в catch) типа, например:
long с = 12;
throw c; // тип исключения - long
throw 12.0; // тип исключения - double
throw (short) 12; // тип исключения - short
В этом случае программа аварийно завершится на выбросе исключения. Например, если сгенерировать вещественное исключение в вышеописанной программе, она отработает так:
Блок try
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
Конкретное сообщение об ошибке зависит от использованного компилятора (в примере использован компилятор из Visual Studio 2005). После вывода сообщения об ошибке ОС Windows предложит закрыть программу или попытаться её отладить.
Существует особая форма записи блока catch, которая позволяет перехватить исключение любого типа:
catch (...) {
// обработка ошибки
}
Такой блок перехватывает любое исключение ничего не зная о его типе. Его имеет смысл ставить только последним catch-блоком, т.к. любые блоки catch после него никогда не будут выполнены.
Обычно после try располагается несколько блоков catch с различными типами исключений. Они отвечают за обработку тех ошибок, которые программист смог предусмотреть и захотел обработать. Последним ставится catch (…), который перехватывает ошибки непредусмотренных типов и позволяет избежать аварийного завершения программы.