Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

osn_progr_final

.pdf
Скачиваний:
37
Добавлен:
12.02.2016
Размер:
3.27 Mб
Скачать

terminate_function _RTLENTRY set_terminate(terminate_function);

Ця функція повертає адресу попередньої функції завершення. Нова функція завершення не повинна повертати управління коду, який її викликає чи викидає виключення.

Функція завершення викликається також і в тому випадку, коли виключення викидається при виконанні деструктора - якщо деструктор викликався в процесі розмотування стека, який був ініційований раніше викинутим виключенням.

2.2.5 Робота з специфікаціями виключень

Можна задавати список виключень, які функція може викидати, за допомогою специфікації виключень, що приєднується до заголовка функції. Специфікація має наступний формат:

throw( тип, тип, ...)

Специфікація виключень без типу вказує, що функція не повинна викидати ніяких виключень. Функції без специфікацій навпаки можуть викидати будь-які виключення. Приклад:

struct xClass{ int i;};

void funcA(void) throw(int){ }

void funcB(void) throw(long,xClass*){ } void funcC(void) throw(){ }

2.2.6 Робота з непередбаченими виключеннями

Специфікації виключень, власне кажучи, ні до чого не зобов’язують. Тобто функція може викинути виключення, яке вона обіцяла не використовувати. При компіляції ніякої помилки не буде:

void func(void) throw(int)

{throw ”Ой!”;}

Порушення списку допустимих виключень виявляється тільки під час виконання. Непередбачене виключення приводить до виклику функції unexpected(). По замовчуванню unexpected() просто викликає функцію terminate(). Однак, за допомогою функції set_unexpected() можна встановити свою власну процедуру, яка буде викликатись в момент викиду неспецифікованого виключення. Функція set_unexpected() визначена в файлі EXCEPT.H:

typedef void(_RTLENTRY *unexpected_function)(); // . . .

unexpected_function _RTLENTRY set_unexpected(unexpected_function);

361

Функція повертає адресу попередньої процедури для непередбачених виключень. Ваша процедура не може повертати управління чи викидати виключення.

2.2.7 Робота з конструкторами та виключеннями

Розглянемо ситуацію, коли виключення прямо чи непрямо викидається конструктором. При виникненні виключення деструктори викликаються лише для повністю сконструйованих локальних об’єктів. Розглянемо приклад.

#include "stdafx.h" #include<iostream.h> class DataClass {public: DataClass()

{cout<<" DataClass:: DataClass()"<<endl;} ~DataClass()

{cout<<" DataClass:: ~DataClass()"<<endl;} };

class BaseClass {public: BaseClass()

{cout<<" BaseClass:: BaseClass()"<<endl;} ~BaseClass()

{cout<<" BaseClass:: ~BaseClass()"<<endl;} };

class DerivedClass:public BaseClass

{DataClass data; public: DerivedClass()

{cout<< "DerivedClass:: DerivedClass()"<<endl; cout<<"викидається виключення"<<endl;

throw "Ой! Щось сталося";}

~DerivedClass()

{cout<<"DerivedClass::~ DerivedClass()"<<endl;} };

int main(void)

{try{ DerivedClass tds;//. . .

}

catch(const char* msg)

{cout<<"впіймалось виключення"<<msg<<endl; return -1;}

362

return 0;}

Результати роботи програми:

BaseClass:: BaseClass()

DataClass:: DataClass() DerivedClass::DerivedClass()

викидаємо виключення

DataClass:: ~DataClass() DerivedClass::~ DerivedClass()

впіймалось виключення: Ой! Щось сталося

Як бачимо за результатами роботи програми, при визначенні екземпляра похідного класу tds викликається конструктор базового класу, потім конструктор DataClass, екземпляр якого оголошений як поле похідного класу (контейнерне відношення) і нарешті констркутор самого похідного класу DerivedClass. У конструкторі похідного класу викидається виключення. І тут викликаються деструктори

~DataClass() та ~ DerivedClass()та управління передаєть-

ся на обробник виключення.

Щоб управління виключеннями С++ приносило вигоду, необхідно запакувати ініціалізацію та очистку деякого ресурса відповідно в конструктор та деструктор класу і при виділенні ресурса створювати локальні екземпляри цього класу. Якщо конструктор класу виділяє ресурси кількох типів чи виконує код, який може викинути виключення після того, як ресурс виділений, рекомендується помістити кожен ресурс у свій власний клас. Таким чином, кожен ресурс завжди буде локалізованим у повністю зконструйованому класі.

2.2.8 Динамічні об’єкти

Якщо виключення викидається з конструктора об’єкта, шо створюється за допомогою операції new, пам’ять автоматично звільняється. Розглянемо наступний прклад:

#include<iostream.h> class TVClass

{

public:

TVClass()

{

cout<< "TVClass:: TVClass()"<<endl;

throw "помилка в конструкторі класу

TVClass";

}

363

~TVClass()

{

cout<<"~TVClass::~TVClass()"<<endl;

}

};

class Tclass: public TVClass

{

int i; public: Tclass(); ~Tclass();

void* operator new(size_t); void operator delete(void *p); };

Tclass::Tclass():TVClass()

{

cout<<"Tclass::Tclass()";

}

Tclass::~Tclass()

{

cout<<" Tclass::~Tclass()"<<endl;

}

void* Tclass::operator new(size_t size)

{

cout<<"Tclass::operator new()"<<endl; char *c= new char[size];

return (c);

}

void Tclass::operator delete(void *p)

{

cout<<"Tclass::operator delete()"<<endl; delete(p);

}

int main(void) {Tclass *tcp; try{tcp=new Tclass;

cout<<“обєкт створений ”<<endl; delete tcp;}

catch(const char *msg)

364

{cout<<“Виключення”<<msg<<endl;} return 0;}

Результати роботи:

Tclass::operator new() TVClass::TVClass() Tclass::operator delete()

Виключення: помилка в конструкторі TVClass

2.2.9Передача значень з конструктора та деструктора

Вмові С++ не можна конструктору чи деструктору повертати

значення. Однак, можна використовувати виключення для того, щоб повідомити з конструктора чи деструктора про стан помилки. І не тільки про стан помилки: можна повернути цілий обєкт. Приклад:

#include<except.h>

#include<iostream.h> class TSomeClass {//. . .

public:

struct xSomeClass {int errCode; xSomeClass(int err){ errCode=err;

}

};

TSomeClass();};

TSomeClass::TSomeClass() {//. . .

if(1)

throw xSomeClass(3);} int main(void)

{try

{TSomeClass tsc; } //. . .}

catch(TSomeClass::xSomeClass xinfo)

{cout<<"Код помилки виключення: " << xinfo.errCode

<<endl;} return 0;}

Результати роботи:

Код помилки виключення 3

2.2.10 Робота з ієрархіями виключень

Як вже відмічалось , можна передбачити обробник для базового класу, який буде перехоплювати будь-які об’єкти, що є public-

365

похідними від базового класу. Утворюючи класи виключень як похідні з загального базового класу, можна використати властивість поліморфізму і перехоплювати вказівник чи посилання на базовий клас. Наприклад, для зображення помилок вводу-виводу можемо використовувати наступні класи:

 

 

TIOError

 

TReadError

 

TSeekError

TWriteError

#include<iostream.h> class TIOError {public:

virtual void explain()

{

//... пояснити проблему

}

};

class TReadError: public TIOError { public:

void explain();

//...

};

class TWriteError: public TIOError { public:

void explain(); // ...

};

int main(void){ try{

// виконати ввід.вивід

}

catch(TIOError& ioerr) {ioerr.explain();} return 0;}

2.2.11 Робота з специфічними класами виключень

Існує кілька визначених класів, які використовуються бібліотекою С++ для повідомлень про виключення. Розглянемо класи xmsg та xalloc.

2.2.11.1 xmsg

Клас xmsg має таку структуру: class _EXPCLASS xmsg {public:

xmsg(const string _FAR &msg);

366

xmsg(const xmsg _FAR &msg); ~ xmsg();

const string _FAR & why() ; void raise() {throw(xmsg);}

xmsg& operator=(const xmsg _FAR &src); private:

string _FAR *str; };

Тоді можемо використовувати цей клас:

#include<iostream.h>

#include<cstring.h>

#include<except.h>

void func() { if {1)

{xmsg xx(“зареєстрована помилка”); throw xx;

}

int main(void) {try{func();//. . .

}

catch( xmsg& msg)

{ cerr<<“виключення”<<msg.why()<<endl; return -1;}

return 0;}

2.2.11.2 xalloc

Стандартна операція new викидає виключення xalloc, якщо пам’ять не може бути виділена. Клас xalloc оголошується так:

class _EXPCLASS xalloc:public xmsg {public:

xalloc( const string _FAR &msg, size_t size); size_t requested() ;

void raise() {throw(xalloc);} private:

size_t size;};

Необхідно розміщувати кожен запит про виділення пам’яті в try-блок з обробником catch(xalloc&):

#include <except.h> #include<iostream.h> void func()

{char *p=0;

try{p=new char[0x100];} catch(xalloc& xl)

367

{cerr<<“перехоплено xalloc”;}

//. . .

}

Відмітимо, що попередні версії операції new при недостачі пам’яті вертали NULL. Щоб відновити таку поведінку, можна викликати фу-

нкцію set_new_handler з параметром NULL:

#include<new.h>

. . .

set_new_handler(NULL);

Файл EXCEPT.H визначає три глобальних змінних, які можна використовувати для отримання інформації про виключення: _throwFileName -вказівник типу char* на ім’я файла, з якого було викинуте виключення

_throwLineNumer - змінна типу unsigned з номером рядка, в якому було викинуте виключення

_throwExceptionName -вказівник на тип char* ( ім’я виключення). Приклад:

#include <except.h> #include<iostream.h> #include<cstring.h> void func(void)

{if(1)

{ xmsg xx(“Тип помилки: ###”);

//. . .

throw xx;}

}

int main(void)

{

try{

func(); //. . .

}

catch(...){ cout<<“Виключення типу”\ <<__throwExceptionName\

<<“викинуте”\ <<“рядок”\ <<__throwLineNumber\

<<“Файл:”\ <<__throwFileName\ <<endl;

return -1;

}

368

return 0;}

Відмітимо, що при роботі з виключеннями в деяких версіях компілятора С++ необхідно встановити відповідні опції компілятора : -x якщо не встановлена, то компілятор не розпізнає try-блоки;

-xp якщо встановлена, то є доступ до імені файла та номера рядка, де було викинуте виключення;

-xd дозволяє виклик деструкторів локальних об’єктів , створених між точками throw i catch.

2.3 СТРУКТУРНЕ УПРАВЛІННЯ ВИКЛЮЧЕННЯМИ

Структурне управління виключеннями є частиною операційної системи

WINDOWS NT. Структурне управління пропонує дві можливості: кадроване управління виключеннями та завершуюче управління.

2.3.1 Використання кадрованого управління виключеннями

(_try/_except)

2.3.1.1 Синтаксис

Кадроване управління виключеннями включає в себе три моме-

нти:

по-перше, є блок кодуякий знаходиться в фігурних дужках і слідує за ключовим словом __try. Він складається з одного чи кількох операторів, які можуть прямо чи непрямо заявляти виключення, за якими слідує ключове слово __except з фільтруючим виразом в якості параметра, за яким (виразом _except ) слідує інший блок коду - блокобробник виключення, також поміщений в фігурних дужках:

__try

{/*тіло*/ } __except(фільтруючий вираз)

{/*блок-обробник виключення*/ }

2.3.1.2 Функція RaiseException()

Щоб заявити виключення, можна використовувати функцію

RaiseException():

void __cdecl __far RaiseException(

DWORD dwExeptionCode, DWORD dwExeptionFlags, DWORD nNumberOfArguments, const LPWORD lpArguments);

де

369

dwExeptionCode - код, що ідентифікує заявлене виключення dwExeptionFlags - показує, чи являється виключення відновлюваним. Може

приймати

значення

EXEPTION_CONTINUABLE

та

EXEPTION_NONCONTINUABLE

 

nNumberOfArguments - вказує кількість аргументів, що передаються в масиві lpArguments

lpArguments - адреса масиву 32-бітних аргументів

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

EXCEPTION_EXECUTE_HANDLER - управління передається на блок-

обробник виключення

EXCEPTION_CONTINUE_SEARCH - управління на асоційований блокобробник не передається. Стек розмотується і відбувається пошук іншого обробника

EXCEPTION_CONTINUE_EXECUTION - пошук припиняється і управління повертається в те місце, де було заявлене виключення Приклад:

#include <excpt.h> #include <stdio.h>

#define EXCEPTION_ERROR_CODE 0x1000L void doSomething(void)

{printf(“намагаємось що-небуть зробити \n”; printf(“Увага! Виявлена помилка \n”); printf(“Увага! Виявлена помилка \n”);

RaiseException( EXCEPTION_ERROR_CODE, EXCEPTION_CONTINUABLE,0,0);} int main()

{ __try{ doSomething();}

__except (EXCEPTION_EXECUTE_HANDLER)

{printf(“Виключення перехоплено...\n”); return 0;}

2.3.1.3 Фільтруючий вираз

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

GetExceptionCode() -повертає код, який ідентифікує заявлене виключення

370

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