Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
84
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

536

Объясните, почему модель обработки исключений в C++ называется невозвратной.

Упражнение 11.5

Даны следующие объявления исключений. Напишите выражения throw, создающие

(a)class exceptionType { }; catch( exceptionType *pet ) { }

(b)catch(...) { }

(c)enum mathErr { overflow, underflow, zeroDivide }; catch( mathErr &ref ) { }

(d)typedef int EXCPTYPE;

объект-исключение, который может быть перехвачен указанными обработчиками: catch( EXCPTYPE ) { }

Упражнение 11.6

Объясните, что происходит во время раскрутки стека. Упражнение 11.7

Назовите две причины, по которым объявление исключения в предложении catch следует делать ссылкой.

Упражнение 11.8

На основе кода, написанного вами в упражнении 11.3, модифицируйте класс созданного исключения: неправильный индекс, использованный в операторе operator[](), должен сохраняться в объекте-исключении и затем выводиться catch-обработчиком. Измените программу так, чтобы operator[]() возбуждал при ее выполнении исключение.

11.4. Спецификации исключений

По объявлениям функций-членов pop() и push() класса iStack невозможно определить, что они возбуждают исключения. Можно, конечно, включить в объявление подходящий комментарий. Тогда описание интерфейса класса в заголовочном файле будет содержать

class iStack { public:

// ...

void

pop( int &value

);

//

возбуждает

popOnEmpty

void

push( int value

);

//

возбуждает

pushOnFull

private:

// ...

документацию возбуждаемых исключений:

};

Но такое решение несовершенно. Неизвестно, будет ли обновлена документация при выпуске следующих версий iStack. Кроме того, комментарий не дает компилятору достоверной информации о том, что никаких других исключений функция не возбуждает.

Спецификация исключений позволяет перечислить в объявлении функции все

С++ для начинающих

537

исключения, которые она может возбуждать. При этом гарантируется, что другие исключения функция возбуждать не будет.

Такая спецификация следует за списком формальных параметров функции. Она состоит из ключевого слова throw, за которым идет список типов исключений, заключенный в скобки. Например, объявления функций-членов класса iStack можно модифицировать,

class iStack { public:

// ...

void pop( int &value ) throw(popOnEmpty); void push( int value ) throw(pushOnFull);

private:

// ...

добавив спецификации исключений:

};

Гарантируется, что при обращении к pop() не будет возбуждено никаких исключений,

кроме popOnEmpty, а при обращении к push()–только pushOnFull.

Объявление исключения это часть интерфейса функции, оно должно быть задано при ее объявлении в заголовочном файле. Спецификация исключений это своего рода контрактмежду функцией и остальной частью программы, гарантия того, что функция не будет возбуждать никаких исключений, кроме перечисленных.

Если в объявлении функции присутствует спецификация исключений, то при повторном объявлении этой же функции должны быть перечислены точно те же типы.

Спецификации исключений в разных объявлениях одной и той же функции не

//два объявления одной и той же функции extern int foo( int = 0 ) throw(string);

//ошибка: опущена спецификация исключений

суммируются:

extern int foo( int parm ) { }

Что произойдет, если функция возбудит исключение, не перечисленное в ее спецификации? Исключения возбуждаются только при обнаружении определенных аномалий в поведении программы, и во время компиляции неизвестно, встретится ли то или иное исключение во время выполнения. Поэтому нарушения спецификации исключений функции могут быть обнаружены только во время выполнения. Если функция возбуждает исключение, не указанное в спецификации, то вызывается unexpected() из стандартной библиотеки C++, а та по умолчанию вызывает terminate(). (В некоторых случаях необходимо переопределить действия, выполняемые функцией unexpected(). Стандартная библиотека предоставляет механизм для этого.

Подробнее см. [STRAUSTRUP97].)

Необходимо уточнить, что unexpected() не вызывается только потому, что функция возбудила исключение, не указанное в ее спецификации. Все нормально, если она обработает это исключение самостоятельно, внутри функции. Например:

С++ для начинающих

538

void recoup( int op1, int op2 ) throw(ExceptionType)

{

try {

// ...

throw string("we're in control");

}

// обрабатывается возбужденное исключение catch ( string ) {

// сделать все необходимое

}

}// все хорошо, unexpected() не вызывается

Функция recoup() возбуждает исключение типа string, несмотря на его отсутствие в спецификации. Поскольку это исключение обработано в теле функции, unexpected() не вызывается.

Нарушения спецификации исключений функции обнаруживаются только во время выполнения. Компилятор не сообщает об ошибке, если в выражении throw возбуждается исключение неуказанного типа. Если такое выражение никогда не выполнится или не возбудит исключения, нарушающего спецификацию, то программа будет работать, как и

extern void doit( int, int ) throw(string, exceptionType);

void action ( int op1, int op2 ) throw(string) { doit( op1, op2 ); // ошибки компиляции не будет

// ...

ожидалось, и нарушение никак не проявится:

}

doit() может возбудить исключение типа exceptionType, которое не разрешено спецификацией action(). Однако функция компилируется успешно. Компилятор при этом генерирует код, гарантирующий, что при возбуждении исключения, нарушающего спецификацию, будет вызвана библиотечная функция unexpected().

Пустая спецификация показывает, что функция не возбуждает никаких исключений:

extern void no_problem () throw();

Если же в объявлении функции спецификация исключений отсутствует, то может быть возбуждено исключение любого типа.

Между типом возбужденного исключения и типом исключения, указанного в

int convert( int parm ) throw(string)

{

//...

if ( somethingRather )

//ошибка программы:

//convert() не допускает исключения типа const char* throw "help!";

спецификации, не разрешается проводить никаких преобразований:

}

С++ для начинающих

539

Выражение throw в функции convert() возбуждает исключение типа строки символов в стиле языка C. Созданный объект-исключение имеет тип const char*. Обычно выражение типа const char* можно привести к типу string. Однако спецификация не допускает преобразования типов, поэтому если convert() возбуждает такое исключение, то вызывается unexpected(). Для исправления ошибки выражение throw можно модифицировать так, чтобы оно явно преобразовывало значение выражения в тип string:

throw string( "help!" );

11.4.1. Спецификации исключений и указатели на функции

Спецификацию исключений можно задавать и при объявлении указателя на функцию. Например:

void (*pf)( int ) throw(string);

В этом объявлении говорится, что pf указывает на функцию, которая способна возбуждать только исключения типа string. Как и для объявлений функций,

спецификации исключений в разных объявлениях одного и того же указателя не

extern void (*pf) ( int ) throw(string);

// ошибка: отсутствует спецификация исключения

суммируются, они должны быть одинаковыми: void (*pf)( int );

При работе с указателем на функцию со спецификацией исключений есть ограничения на тип указателя, используемого в качестве инициализатора или стоящего в правой части присваивания. Спецификации исключений обоих указателей не обязаны быть идентичными. Однако на указатель-инициализатор она должна накладывать столь же или более строгие ограничения, чем на инициализируемый указатель (или тот, которому

void recoup( int, int ) throw(exceptionType); void no_problem() throw();

void doit( int, int ) throw(string, exceptionType);

//правильно: ограничения, накладываемые на спецификации

//исключений recoup() и pf1, одинаковы

void (*pf1)( int, int ) throw(exceptionType) = &recoup;

//правильно: ограничения, накладываемые на спецификацию исключений no_problem(), более строгие,

//чем для pf2

void (*pf2)( ) throw(string) = &no_problem;

//ошибка: ограничения, накладываемые на спецификацию

//исключений doit(), менее строгие, чем для pf3

//

присваивается значение). Например:

void (*pf3)( int, int ) throw(string) = &doit;