Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
+ООП_Навч_посібник.doc
Скачиваний:
7
Добавлен:
01.07.2025
Размер:
6.58 Mб
Скачать

18.2.3. Повторне генерування винятку

Для того, щоб повторно згенерувати винятки в його обробнику, скористайтеся throw-настановою без вказання типу винятки. У цьому випадку поточний виняток буде передано в зовнішню try/catch-послідовність. Найчастіше причиною для такого виконання настанови throw слугує прагнення дати змогу доступ до одного винятку декільком обробникам. Наприклад, перший обробник винятків повідомлятиме про один аспект винятку, а другий – про інше. Винятки можна повторно згенерувати тільки в catch-блоці (або в будь-якій функції, які викликається з цього блоку). При повторному генеруванні виняток не перехоплюватиметься тією ж catch-настановою. Воно розповсюдиться на найближчу try/catch-послідовність.

Повторне генерування винятку типу char * продемонстровано у наведеному нижче коді програми.

Код програми 18.11. демонстрація повторного генерування винятку типу char *

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

void Xhandler() {

try {

throw "Привіт"; // Генерує винятки типу char *

}

catch(char *) { // Перехоплює винятки типу char *

cout << "Перехоплення винятку у функції Xhandler.\n";

throw; // Повторне генерування винятку типу char *,

// яке буде перехоплене поза функцією Xhandler.

}

}

int main()

{

cout << "Початок.\n";

try {

Xhandler();

}

catch(char *) {

cout << "Перехоплення винятку у функції main().\n";

}

cout << "Кінець програми";

getch(); return 0;

}

У процесі виконання ця програма відображає на екрані такі результати:

Початок.

Перехоплення винятку у функції Xhandler.

Перехоплення винятку у функції main().

Кінець програми

18.3. Оброблення винятків, згенерованих оператором new

У розд. 9 було сказано, що оператор new генерує винятки, якщо не вдається задовольнити запит на виділення пам'яті. Оскільки тема генерування винятків розглядається тільки у цьому розділі, то опис оброблення винятків цього типу був відкладений "на потім". Ось тепер настав час про це поговорити.

Спершу необхідно відзначити, що у цьому розділі описується поведінка оператора new відповідно до стандарту мови C++. Як було відзначено в розд. 9, дії, що виконуються системою при неуспішному використанні оператора new, з моменту винаходу мови C++ змінювалися вже кілька разів. Спочатку оператор new повертав при невдачі значення NULL. Пізніше така поведінка була замінена генеруванням винятків. Окрім того, кілька разів мінялося ім'я цього винятки. Нарешті, було вирішено, що оператор new генеруватиме винятки за замовчуванням, але як альтернативний варіант він може повертати і нульовий покажчик. Отже, оператор new у різний час був реалізований різними способами. І хоча всі сучасні компілятори реалізують оператор new відповідно до стандарту мови C++, компілятори "поважнішого" віку можуть містити відхилення від нього. Якщо наведені тут приклади кодів програм не працюють з Вашим компілятором, то зверніться до документації, що додається до компілятора, і поцікавтеся, як саме він реалізує функціонування оператора new.

Згідно з стандартом мови C++, у випадку неможливості задовольнити запит на виділення пам'яті, потрібної оператором new, генерується виняток типу bad_alloc. Якщо Ваша програма не перехопить його, то вона буде достроково завершена. Хоча така поведінка годиться для коротких прикладів програм, в реальних додатках необхідно перехоплювати цей виняток і розумно обробляти його. Щоб отримати доступ до винятку типу bad_alloc, потрібно залучити до програми заголовок <new>.

Розглянемо приклад використання оператора new, поміщеного в try/catch-блок для вистежування невдалих результатів запиту на виділення пам'яті.

Код програми 18.12. Демонстрація оброблення винятків, що генеруються оператором new

#include <iostream> // Для потокового введення-виведення

#include <new> // Для перевантаження операторів new і delete

using namespace std; // Використання стандартного простору імен

int main()

{

int *p, i;

try {

p = new int[32]; // Запит на виділення пам'яті

// для 32-елементного int-масиву

}

catch(bad_alloc ха) {

cout << "Пам'ять не виділена.\n";

return 1;

}

for(i=0; i<32; i++) p[i] = i;

for(i=0; i<32; i++) cout << p[i] << " ";

delete [] p; // Звільнення пам'яті

getch(); return 0;

}

Під час невдалого виконанні оператора new виняток у цій програмі буде перехоплено catch-настановою. Цей же підхід можна використовувати для вистежування будь-яких помилок, взаємопов'язаних з використанням оператора new: достатньо помістити кожну new-настанову в try-блок.

Альтернативна форма оператора new – nothrow

Стандарт мови C++ при невдалій спробі виділення пам'яті замість генерування винятку також дає змогу оператору new повертати значення NULL. Ця форма використання оператора new особливо корисна у процесі компілювання старих програм із застосуванням сучасного С++-компілятора. Цей засіб також дуже корисний при заміні викликів функції malloc() оператором new. Це звичайна практика у процесі перекладу С-коду програми на мову програмування C++. Отже, цей формат оператора new має такий вигляд:

p_var = new(nothrow) тип;

У цьому записі елемент p_var – це покажчик на змінну типу тип. Цей nothrow-формат оператора new працює подібно до оригінальної версії оператора new, яка використовувалася кілька років тому. Оскільки оператор new(nothrow) повертає при невдачі значення NULL, його можна "упровадити" в старий код програми, не вдаючись до оброблення винятків. Проте в нових програмах на C++ все ж таки краще мати справу з винятками.

У наведеному нижче прикладі показано, як використовується альтернативний варіант new(nothrow). Неважко здогадатися, що нижче наведено варіацію на тему попередньої програми.

Код програми 18.13. Демонстрація механізму використання nothrow-версії оператора new

#include <iostream> // Для потокового введення-виведення

#include <new> // Для перевантаження операторів new і delete

using namespace std; // Використання стандартного простору імен

int main()

{

int *p, i;

p = new(nothrow) int[32]; // Використання nothrow-версії

if(!p) {

cout << "Пам'ять не виділена.\n";

return 1;

}

for(i=0; i<32; i++) p[i] = i;

for(i=0; i<32; i++) cout << p[i] << " ";

delete [] p; // Звільнення пам'яті

getch(); return 0;

}

У цій програмі під час використання throw-версії після кожного запиту на виділення пам'яті необхідно перевіряти значення покажчика, що повертається оператором new.