Конструкторы и функциональные try-блоки
Можно объявить функцию так, что все ее тело будет заключено в 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. Его конструктор можно переопределить так:
inline 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().
Использование функционального try-блока – это единственное решение, гарантирующее, что все исключения, возбужденные при создании объекта, будут перехвачены в конструкторе. Для конструктора класса Account такой try-блок можно определить следующим образом:
inline Account:: Account( const char* name, double opening_bal ) try : _balance( opening_bal - ServiceCharge() ) { _name = new char[ strlen(name) + 1 ]; strcpy( _name, name ); _acct_nmbr = get_unique_acct_nmbr(); |
catch (...) { // теперь специальная обработка // перехватывает исключения, // возбужденные в ServiceCharge() |
Обратите внимание, что ключевое слово try
находится перед списком инициализации членов, а составная инструкция, образующая try-блок, охватывает тело конструктора. Теперь предложение catch(...) принимается во внимание при поиске обработчика исключения, возбужденного как в списке инициализации членов, так и в теле конструктора.