Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++Lecture-1-15.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
122 Кб
Скачать
☆

Int main()

{ int year;

double total = 0.0;

LimitArE income(Years,FirstYear);

cout<<"Enter your income for the last "<<Years<<" years:\n";

try {

for (year=FirstYear; year<FirstYear+Years; ++year)

{ cout<<"Year "<<year<<": $";

cin>>income[year];

}

cout.precision(2); cout.setf(ios_base::showpoint);

cout.setf(ios_base::fixed,ios_base::floatfield);

cout<<"Recapitulating, here are the figures:\n";

for (year=FirstYear; year<=FirstYear+Years; ++year)

{ cout<<year<<": $"<<income[year]<<'\n';

total += income[year];

}

}

catch (LimitArE::SonOfBad& bi)

{ cout<<"\nLimitArE exception: "<<bi.badInd<<" is a bad index value\n"

<<"Index should be in the range "<<bi.l_lim

<<" to "<<bi.u_lim<<".\n";

cout<<"\n . . . from the E-object:\n"; bi.report();

}

catch (LimitArE::BadIndex& bi)

{ cout<<"\nArrayDbE exception: "<<bi.badInd<<" is a bad index value\n";

cout<<"\n . . . from the E-object:\n"; bi.report();

}

cout<<"\nTOTAL INCOME for "<<(year-FirstYear)<<" years is $"<<total<<".\n";

cout<<"Bye!\n"; return 0;

}

Якщо клас використовує, як в цьому прикладі, винятки, описані ієрархією класів, то блоки перехоплення розташовують у певному порядку: від конкретніших типів до загальніших. Тобто, спочатку перехоплюють винятки, описані дочірнім класом, пізніше – батьківським.

Згадаємо про ще одну можливість щодо опрацювання винятків – повторне генерування винятків. Якщо до виняткової ситуації привів ланцюжок викликів декількох функцій і є потреба опрацьовувати виняток у кожній з них, в блоках опрацювання використовують інструкцію throw без параметрів для повторного запуску щойно опрацьованого винятку. Наприклад:

class Ex {};

void first() throw(Ex);

void second() throw(Ex);

Int main() {

try { first(); }

catch(Ex) { cout<<"Main work up\n"; }

return 0;

}

void first() throw(Ex) {

try { second(); }

catch(Ex) { cout<<"First work up\n"; throw; }

}

void second() throw(Ex) {

do_something;

if (cond) throw Ex();

}

Згенерований в тілі second() виняток зазнає опрацювання в catch-блоці функції first(), але одразу ж буде запущений повторно. Наступне опрацювання відбудеться в main().

Використання винятків допомагає виправити помилки, але може стати причиною нових. Якщо функція захопила якийсь ресурс (файл, пам’ять, пристрій тощо), то перед завершенням вона мала б його звільнити. Що станеться, якщо виняток трапиться раніше? Розглянемо приклад оголо­шення функції, що використовує для запису деякий файл.

void use_file(const char* fn) {

FILE* f = fopen(fn,"w"); // відкриваєм для запису файл з іменем fn

// працюємо з файлом f, тут може скластися ситуація, що запускає виняток

// тоді керування до наступної інструкції не перейде, файл залишиться відкритим

fclose(f);

}

Спробуємо виправити код і гаратувати закривання файла:

void use_file(const char* fn) {

FILE* f = fopen(fn,"w");

try {

// працюємо з файлом f, тут може статися throw виняток

}

catch (...) { fclose(f); // що б не сталося, файл закриваємо

throw; } // і повідомляємо про виняток далі

fclose(f); // нормальне закривання файла

}

Такі заплутані алгоритми не бувають надійними. Нам потрібно, щоб функція закрила файл перед завершенням своєї роботи незалежно від того, чому це завершення відбулось (штатно чи завдяки запуску винятка). Саме так спрацьовують деструктори об’єктів: і виконання return, і виконання throw в тілі функції зумовлює вилучення з автоматичної пам’яті всіх локальних змінних (для вилучення об’єктів автоматично викликаються деструктори). Тому запит на використання ресурсу та його звільнення потрібно інкапсулювати в класі:

class FilePtr {

FILE* p;

public:

FilePtr(const char* n, const char* a) { p = fopen(n,a); } // захопити

FilePtr(FILE* pp) { p = pp; }

~FilePtr() { fclose(p); } // звільнити

operator FILE*() { return p; }

};

Тепер функція використання файла запишеться стисло і ясно:

void use_file(const char* fn) {

FilePtr f(fn,"w");

// сміливо працюємо з файлом f

}

Звичайно, якщо взаємодію з файлом організувати за допомогою потокових об’єктів, то Вам не доведеться турбуватися про власні класи – все вже є в стандартних потокових класах.

Описану техніку використання ресурсу можна використати для керування динамічною пам’яттю:

class DoublePtr {

double * p;

public:

DoublePtr() { p = new double; }

~DoublePtr() { delete p; }

double& operator*() { return *p; }

};

Оголошення змінної типу DoublePtr автоматично створить нову динамічну змінну дійсного типу. Як тільки ця змінна перестане існувати, динамічна пам’ять автоматично звільниться.

На завершення згададаємо про ще одну можливість використання винятків. Вони не мусять сигналізувати про помилки! Ви можете використати throw myObject для того, щоб швидко припинити виконання цілого ланцюжка викликаних функцій і транспортувати дані в запущеному об’єкті з «найглибшої» функції кудись нагору, де цей об’єкт буде перехоплено. Такий спосіб використання винятка можна порівняти з використанням інструкції переходу назовні з цілої групи глибоко вкладених (один в одного) циклів. Суть така ж: швидко передати керування, оминаючи звичайні шляхи. Зрозуміло, що для застосування таких «екстремальних» підходів потрібні вагомі причини.

10

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]