// гарантируется, что исключения возбуждены не будут

void compute( Base *pb ) throw()

{

try {

pb-f3( ); // может возбудить исключение типа int или string

}

// обработка исключений, возбужденных в Base::f3()

catch ( const string & ) { }

catch ( int ) { }

}

Объявление f3() в классе Base гарантирует, что эта функция возбуждает лишь исключения типа int или string. Следовательно, функция compute() включает catch-обработчики только для них. Поскольку спецификация исключений f3() в производном классе Derived накладывает больше ограничений, чем в базовом Base, то при программировании в согласии с интерфейсом класса Base наши ожидания не будут обмануты.

В главе 11 мы говорили о том, что между типом возбужденного исключения и типом, заданным в спецификации исключений, не допускаются никакие преобразования. Однако если там указан тип класса, то функция может возбуждать исключения в виде объекта класса, открыто наследующего заданному. Аналогично, если имеется указатель на класс, то функции разрешено возбуждать исключения в виде указателя на объект класса, открыто наследующего заданному. Например:

class stackExcp : public Excp { };

class popObEmpty : public stackExcp { };

class pushOnFull : public stackExcp { };

void stackManip() throw( stackExcp )

{

// ...

}

Спецификация исключений указывает, что stackManip() может возбуждать исключения не только типа stackExcp, но также popOnEmpty и pushOnFull. Напомним, что класс, открыто наследующий базовому, представляет собой пример отношения ЯВЛЯЕТСЯ, т.е. является частным случае более общего базового класса. Поскольку popOnEmpty и pushOnFull – частные случаи stackExcp, они не нарушают спецификации исключений функции stackManip().

<p>19.2.7. Конструкторы и функциональные try-блоки</p>

Можно объявить функцию так, что все ее тело будет заключено в try-блок. Такие try-блоки называются функциональными. (Мы упоминали их в разделе 11.2.) Например:

int main() {

try {

// тело функции main()

}

catch ( pushOnFull ) {

// ...

}

catch ( popOnEmpty ) {

// ...

}

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

Функциональный try-блок необходим для конструкторов класса. Почему? Определение конструктора имеет следующий вид:

имя_класса( список_параметров )

// список инициализации членов:

: член1(выражение1 ) , // инициализация член1

член2(выражение2 ) , // инициализация член2

// тело функции:

{ /* ... */ }

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

Рассмотрим еще раз класс Account, описанный в главе 14. Его конструктор можно переопределить так:

nline Account::

Account( const char* name, double opening_bal )

: _balance( opening_bal - ServiceCharge() )

{

_name = new char[ strlen(name) + 1 ];

strcpy( _name, name );

_acct_nmbr = get_unique_acct_nmbr();

}

Функция ServiceCharge(), вызываемая для инициализации члена _balance, может возбуждать исключение. Как нужно реализовать конструктор, если мы хотим обрабатывать все исключения, возбуждаемые функциями, которые вызываются при конструировании объекта типа Account?

Помещать try-блок в тело функции нельзя:

inline Account::

Account( const char* name, double opening_bal )

: _balance( opening_bal - ServiceCharge() )

{

try {

_name = new char[ strlen(name) + 1 ];

strcpy( _name, name );

_acct_nmbr = get_unique_acct_nmbr();

}

catch (...) {

// специальная обработка

// не перехватывает исключения,

// возбужденные в списке инициализации членов

}

}

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

Перейти на страницу:

Похожие книги