Лекция: Загальна схема обробки виключень
Мова C++ надає простий механізм обробки виключень всіх типів. Якщо обробник для якого-небудь виключення не передбачений, то стандартний обробник генерує повідомлення про помилку і завершує виконання програми. При обробці виключення можна або виправити помилки і зробити можливим подальше виконання програми, або не робити дій по відновленню програми, а лише коректно завершити програму.
4.1.1 Інструкція throw
Незалежно від причини та механізму виявлення виключення, його генерація зводиться до явного або неявного виконання інструкції throw, в якій може бути заданий якийсь вираз:
throw expr;
Вираз expr може бути константою, змінною, екземпляром структури або об'єднання або ж об'єктом класу. Воно призначено для передачі інформації про виключення, яка може використовуватися обробником цього виключення.
Наприклад, в приведеному нижче фрагменті програми при некоректному виділенні пам'яті генерується виключення з рядком, що пояснює помилку:
char *Buffer = new char [1000];
if (Buffer == 0)
throw «out of memory»; // брак пам'яті
Якщо виключення згенероване, але в програмі не передбачена його обробка, механізм виключень викликає з динамічної бібліотеки функцію завершення terminate(), яка видає повідомлення «Abnormal program termination» і припиняє виконання програми.
4.1.2 Блоки try і catch
Для обробки виникаючих виключень в програмі необхідно передбачити інструкції try і catch в наступній послідовності:
try
{
// послідовність інструкцій, при виконанні
// яких контролюється виникнення виключень
}
catch (тип1 arg)
{
// послідовність інструкцій,
// що обробляють виключення типу 1
}
catch (тип2 arg)
{
// послідовність інструкцій,
// що обробляють виключення типу 2
}
…
catch (типN arg)
{
// послідовність інструкцій,
// що обробляють виключення типу N
}
Якщо виключення генерується в будь-якому місці блоку програми, наступному за інструкцією try або всередині будь-якої функції, що викликається в цьому блоці, то управління передається за межі блоку try.
Якщо за try слідує відповідний блок catch, то управління переходить до нього.
Кожний блок обробки виключень починається з інструкції catch, яка містить оголошення в круглих дужках. Якщо тип аргументу в цьому оголошенні співпадає з типом значення в інструкції throw, що згенерувала виключення, то управління передається даному блоку catch.
При невідповідності типів аргументів програма шукає інший обробник з типом аргументу, співпадаючим з типом значення виключення.
Після виконання коду в блоці catch управління передається першій інструкції, наступній за всіма блоками catch, і програма відновлює роботу в нормальному режимі, якщо блок catch не містить оператора return або виклику функції exit().
Таким чином, інструкції try і catch запобігають завершенню програми стандартним обробником виключень.
Блок try називають розділом коду, що охороняється. Якщо виключення при виконанні блоку не виникне, то потік управління перестрибне всі блоки catch і перейде на першу, наступну за ними, інструкцію.
4.1.3 Установка опції для включення механізму обробки виключень в програмах на Visual C++
При роботі в системі програмування Visual C++ для включення механізму обробки виключень в програмі, що розробляється, в конфігурації проекту має бути встановлена в Yes опція «Enable C++ Exceptions». При створенні нового проекту цей режим для обох конфігурацій (тестування і виконання) задається за умовчанням.
Щоб змінити установку, в меню Project потрібно вибрати підменю “ім'я_проекту Properties...”. У лівому вікні діалогу, що з'явився, потрібно відкрити папку Configuration Properties, якщо вона не відкрита, потім папку C/C++, після чого вибрати опцію Code Generation. Потім в таблиці, що з'явилася справа, змінити на потрібну установку рядка з написом Enable C/C++ Exceptions.
4.1.4 Приклад використання всіх інструкцій обробки виключень
Нижче наведений приклад обробки виключення з використанням усіх трьох інструкцій управління виключеннями:
try
{
// інструкції
char *Buffer = new char[1000];
if (Buffer == 0)
throw «out of memory»; // генерація виключення
// інструкції
}
catch (char *ErrorMsg)
{ // обробка виключення
cout << ErrorMsg << '\n';
// обробка помилки й відновлення виконання програми або
// виклик функції exit() для завершення програми
}
// тут виконання програми продовжується
В приведеному прикладі інструкція throw в якості виразу містить рядок «out of memory». Оскільки тип аргументу в інструкції catch (char * ErrorMsg)такий же, як і в інструкції throw, то даний блок catch одержує управління при генерації цього виключення.
В цьому прикладі інструкція catch оголошує аргумент типу char * з ім'ям ErrorMsg, що дозволяє всередині блоку catch отримати доступ до значення, заданого в інструкції throw.
Даний механізм дуже схожий на механізм передачі аргументів функції. Щоб упевнитися в цьому, потрібно представити інструкцію throw як виклик функції, в якому значення «Out of memory» передається як аргумент:
throw («out of memory»);
Блок catch в такому уявленні грає роль функції, що викликається, а оголошення char *ErrorMsg як оголошення її формальних аргументів. Як і аргументи функції, змінна ErrorMsg доступна тільки усередині блоку catch.
Так само як і у функцій, в інструкції catch може знаходитися оголошення типу без імені аргументу:
catch(char *)
{
// значення, задане в інструкції
// throw, використовуватися не може
}
Блок catch в цьому випадку, як і раніше, одержить управління по оператору throw «Out of memory», але без можливості доступу до переданого значення типу char*.
Якщо інструкція catch задає який-небудь інший тип аргументу (наприклад, int), блок не одержить управління при виникненні даного виключення:
catch (int ErrorCode)
{
// управління до цього блоку не перейде при генерації
// виключення інструкцією throw «out of memory»;
}
4.1.5 Управління декількома різнотипними виключеннями
Якщо за блоком try помістити декілька блоків catch, то можна управляти різними типами виключень. Наприклад, в наступному фрагменті програми обробляються виключення з аргументами типу char * або int:
try
{
…
throw «string»; // це виключення обробляється
// першим блоком catch
…
throw 5; // це виключення обробляється
// другим блоком catch
}
catch(char *ErrorMsg)
{
// обробка будь-якого виключення типу char *
}
catch (int ErrorCode)
{
// обробка будь-якого виключення типу int
}
Якщо оголошення блоку catch містить як аргумент три крапки, то такий блок одержує управління у відповідь на виключення будь-якого типу, які сгенеровані попереднім блоком try:
catch (...)
{
// одержує управління у відповідь
// на виключення будь-якого типу
}
Оскільки даний блок не містить оголошень аргументів, він не має доступу до значення, переданого під час виклику виключення.
Блок catch з трьома крапками повинен записуватися останнім.Програма шукає блоки catch в порядку їх проходження і активізує перший блок, що відповідає типу виразу у виключенні, яке виникло. Оскільки блок catch з трьома крапками є останнім, то в першу чергу управління одержує catch з точно співпадаючим типом виключення, а не універсальний блок з трьома крапками.
Як тільки при виконанні програми виникає виняткова ситуація, подальші інструкції всередині блоку try не виконуються. Після виконання блоку catch управління передається інструкції, яка слідує безпосередньо за останнім блоком catch, після чого програма може продовжувати своє нормальне виконання.