Програмування. Структурний підхід (КПІ)
.pdf
Комп’ютерний практикум №7 Обробка виключень
Змінити логіку роботи функції terminate() можна за допомогою функції set_terminate(), а функції unexpected() — за допомогою функції set__unexpected().
Блок catch може обробити більше одного джерела виключень.
Для уточнення опису функції до її прототипу та визначення можна додати визначення виключень, які вона може генерувати, що складається з ключового слова throw і наступного за ним в дужках списку типів виключень.
double mean(double a, double b) throw(char *);
При цьому, по-перше, компіляторові передається інформація про те, якого роду виключення можуть бути згенеровані функцією. Якщо функція згенерує інший вид виключення, буде викликана функція abort(). Подруге, використання специфікації виключень дозволяє будь-якому користувачеві зробити висновок щодо їх переліку і, таким чином, використати цю інформацію у своїй програмі. Наприклад, наступний прототип описує функцію, що може генерувати як виключення char *, так і виключення double.
double multipleErrorsFunction(double z) throw(char *, double);
Слід зазначити, що інформація про виключення повинна міститися як у прототипі, так і у заголовку функції. При цьому порожні дужки у специфікації виключень вказують на те, що функція не генерує виключень.
double simple(double z) throw (); // не генерує виключень
Якщо блок try є вкладеним, можна забезпечити, що пов’язані з ним оброблювачі виключень будуть передавати керування оброблювачам
91
Комп’ютерний практикум №7 Обробка виключень
виключень зовнішнього блоку try. Щоб перехопити будь-яке виключення, замість вказівки типу виключень слід використовувати три крапки (...):
catch(...)
{
// оператори
}
Щоб передати керування зовнішньому блоку try, слід використовувати ключове слово throw без параметру.
catch (char* s)
{
cout << "Exception caught in inner loop." <<; throw;
//перенаправлення до зовнішнього блоку try
}
У наведеному фрагменті програмного коду на екран виводиться повідомлення, після чого керування передається зовнішньому блоку try, де знову буде виконано пошук оброблювача виключень, що відповідає згенерованому виключенню.
Існує декілька способів включення одного блоку try в інший. Перший спосіб полягає у вкладенні одного блоку в інший. Другий спосіб полягає в тому, що один блок try містить виклик функції, що активізує функцію з іншим блоком try. У першому випадку керування буде передано зовнішньому блоку try. У другому випадку для пошуку наступного блоку try буде виконано процедуру розгортання стека.
92
Комп’ютерний практикум №7 Обробка виключень
Розгортання стека
Припустимо, що блок try не містить прямого виклику функції, що генерує виключення, але викликає функцію, що у свою чергу викликає функцію, що генерує виключення. Виконання програми передається від функції, у якій генерується виключення, функції, що містить блок try та відповідні оброблювачі виключень catch. Для цього виконується процедура розгортання стека.
В мові C++ виклики та повернення функцій обробляються наступним чином. При виклику функції інформація розміщається в стеку. Зокрема, у стеку програма розміщає адресу інструкції, що викликала функцію (адресу повернення). При завершенні виконання функції, що була викликана, програма використовує зазначену адресу як адресу, з якого продовжується виконання програми. При виклику функції всі її аргументи також записуються в стек, де вони одержують статус змінних з автоматичним класом пам’яті. Якщо у функції, що викликається, створюються нові змінні з автоматичним класом пам’яті, вони, у свою чергу, також додаються в стек. Якщо у даній функції викликається інша функція, то її інформація також додається в стек і т.д. Коли завершується виконання функції, керування передається за адресою, що була збережена при виклику функції, при цьому вершина стека звільняється. Таким чином, після виконання функції відбувається повернення до тієї функції, що її викликала, і т.д., причому при завершенні роботи кожної з таких функцій відбувається звільнення змінних з автоматичним класом пам’яті. Якщо змінні з автоматичним класом пам’яті є об’єктами класу, то викликається деструктор класу (якщо такий є).
Більш докладно робота з класами та об’єктами розглядається у курсі
Об’єктно-орієнтоване програмування.
93
Комп’ютерний практикум №7 Обробка виключень
Тепер припустимо, що виконання функції переривається не в результаті повернення керування функції, що її викликала, а в результаті виконання блоку обробки виключення. Тоді програма так само, як у випадку нормального повернення, видаляє значення зі стека. Але замість того, щоб зупинитися на першій адресі повернення в стеку, програма буде продовжувати звільняти стек, поки не досягне адреси повернення, що перебуває у відповідному блоці try. Після цього керування передається оброблювачам виключень наприкінці цього блоку, а не першому операторові, розташованому зразу за оператором виклику функції. Цей процес називається розгортанням стека. Однією з важливих особливостей механізму генерації виключень є те, що як і при поверненні функцій викликаються деструктори класів для будь-яких автоматичних об’єктів, розташованих в стеку. При цьому при поверненні керування з функції обробляються лише об’єкти, розміщені в стеку цією функцією, у той час як механізм обробки виключень обробляє об’єкти, що були розміщені в стеку при виконанні цілого ланцюга викликів функцій між блоком try та місцем генерації виключень. При відсутності властивості розгортання стека при генерації виключень залишилися б невикликаними деструктори для автоматичних об’єктів класів, розміщених у стеку при реалізації проміжних викликів всіх відповідних функцій.
Робота з параметрами командного рядка
Для забезпечення передачі даних у програму на мові С++ через командний рядок операційної системи можна скористатися двома параметрами функції main():
void main(int argc, char *argv[])
94
Комп’ютерний практикум №7 Обробка виключень
При цьому у першому параметрі argc буде розміщено кількість елементів (параметрів) масиву вказівників argv на символьні рядки. При цьому кожний символьний рядок відповідає окремому параметру командного рядка. Слід зазначити, що всі значення, що передаються у програму через командний рядок, є строковими. Тому при передачі значень інших типів у програмі необхідно попередньо виконати їх перетворення у потрібний тип даних. У наступному прикладі параметр argc використовується для виводу кількості аргументів, переданих через командний рядок операційної системи.
Приклад 7.1. Використання аргументів, переданих через командний рядок
#include <iostream> using namespace std;
void main(int argc, char *argv[])
{
cout << "Кількість аргументів командного рядка"; cout << " дорівнює " << argc << endl;
}
Якщо ім’ям файлу з програмним кодом є test_params.cpp, то після успішної компіляції створену програму можна запустити з командного рядку та передати необхідні дані наступним чином.
c:\test_params 1 2 “Third string parameter”
Аргументи функції main(), які згруповано всередині подвійних лапок, будуть інтерпретуватись як один аргумент.
95
Комп’ютерний практикум №7 Обробка виключень
Параметр argv[0] завжди містить ім’я виклику програми, так що значенням змінної argc як мінімум є 1.
Як вже зазначалось, другий параметр функції main() з ім’ям argv є масивом вказівників на символьні рядки з окремими частинами командного рядка (переданими параметрами). У наступному прикладі оператор for використовується для виводу елементів масиву argv. (Програма виводить кожний елемент, поки значення змінної циклу не стане більше, ніж argc).
Приклад 7.2. Використання аргументів, переданих через командний рядок
#include <iostream> using namespace std;
void main(int argc, char *argv[]) |
|
{ |
|
int i; |
|
for (i = 0; i < argc; i++) |
<< "] містить "; |
cout << "argv[" << i |
|
cout << argv[i] << endl; |
|
} |
|
У мові C++ для помітки закінчення масиву argv використовується символ NULL. У наступному прикладі за допомогою цикла for виконується прохід по елементах масиву argv, поки його поточний елемент не буде містити значення NULL.
Приклад 7.3. Використання аргументів, переданих через командний рядок
96
Комп’ютерний практикум №7 Обробка виключень
#include <iostream> using namespace std;
void main(int argc, char *argv[])
{
int i;
for (i = 0; argv[i] != NULL; i++)
{
cout << "argv[" << i << "] містить "; cout << argv[i] << endl;
}
Оскільки параметри командного рядка програми на мові С++ передаються як вказівники на рядки, найчастіше їх потрібно перетворювати в необхідний тип. Для цього можна скористатися одною з функцій родини atof(), atoi(), atol(). Повний перелік та опис цих функцій можна знайти в заголовних файлах math.h та stdlib.h.
Ці функції дозволяють перетворити символьний рядок у значення з плаваючою крапкою подвійної точності (функція atof()), у ціле значення (функція atoi()) або в довге ціле значення (функція atol()). Функції припиняють зчитування вихідного рядка після появи першого символу, що не може бути інтерпретований як частина числа (наприклад, символу NULL, що завершує рядок). Функція atof() припускає, що її параметр-рядок може мати степеневе представлення
[whitespace][sign][digits][{d|D|E}[sign]digits]. Функції atoi() та atol() не обробляють десяткові крапки або показники ступеня. Кожна з функцій повертає значення типу double, int або long, отримане в результаті інтерпретації вхідних символів як чисел. Значення, що повертається, дорівнює 0 (0L для функції atol()), якщо вхідний
97
Комп’ютерний практикум №7 Обробка виключень
параметр не може бути перетворений у значення даного типу. Значення, що повертається, у випадку переповнення є невизначеним.
Зворотне перетворення з числового представлення у строкове дозволяють виконати функції ecvt(), fcvt(), gcvt().
У наступних прикладах ілюструється перетворення строкових представлень чисел в числові значення з використанням функцій atof(), atoi(), atol().
Приклад 7.4. Використання функцій atof(), atoi(), atol()
#include <stdlib.h>
main( )
{
char *s; double x; int i; long l;
s=" -2309.12E-15"; x=atof(s);
cout << “Число з плаваючою крапкою: ”<< x; cout << endl;
s="7.8912654773d210";
x=atof(s);
cout << “Число з плаваючою крапкою: ”; cout << x << endl;
s="-9885";
98
Комп’ютерний практикум №7 Обробка виключень
i=atoi(s);
cout << “Ціле”<< i << endl;
s="98854 dollars"; l=atol(s);
cout << ”Довге ціле: ” << l << endl;
}
Більш докладно теоретичні питання, пов’язані з даним комп’ютерним практикумом, викладено у [3].
7.2.Приклад
Унаступному прикладі показано, як в мові C++ функціонує система обробки виняткових ситуацій.
Приклад 7.5. Обробка виняткових ситуацій
#include <iostream> #include<conio.h> using namespace std;
int main()
{
int a;
char* b = "test string for exception generation"; cout << "Begining..." << endl;
//cout << b; try {
// Блок try
99
Комп’ютерний практикум №7 Обробка виключень
cin >> a;
cout << "Inside try block" << endl;
if(!a) throw 10; // Генерація виключення 10 if(a == -1) throw 10.;
// Генерація виключення 10. else throw b;
// Генерація виключення зі строковим ідентифікатором
cout << "This statement can't to be executed";
}
catch (int i)
{ // Оброблювач 1
cout << "Error with number: "; cout << i << endl;
}
catch (char* b)
{ // Оброблювач 2 cout << "String error: "; cout << b << endl;
}
catch(...)
{// Узагальнений оброблювач
cout << "Unknown error 10.!" << endl; cout << "Program finished ...";
}
getch(); return 0;
}
100
