|
Программирование >> Инициализация объектов класса, структура
новые определения классов, вкчающие виртуальнхе функции class Excp { public: . . Произошло << endl; virtual void print( string msg ) { cerr << Произошло исключение class stackExcp : public Excp { }; class pushCnFull : public stackExcp { public: virtual void print() { cerr << попытка поместить значение << value << в полн стек\n ; int main() { try { iStack::push() возбуждает исключение pushCnFull } catch ( Excp eCbj ) { eCbj.print(); хотим вызвать виртуальна функцию, но вызается экземпляр из базового Функцию print() теперь можно использовать в catch- обработчике следующим образом: Хотя возбужденное исключение имеет тип pushCnFull, а функция print() виртуальна, инструкция eCbj.print() печатает такую строку: Произошло исключение Вызываемая print() является членом базового класса Excp, а не замещает ее в производном. Но почему? Вспомните, что объявление исключения в catch-обработчике ведет себя почти так же, так объявление параметра. Когда управление попадает в catch- обработчик, то, поскольку в нем объявлен объект, а не ссылка, eCbj инициализируется копией подобъекта Excp базового класса объекта исключения. Поэтому eCbj - это объект типа Excp, а не pushCnFull. Чтобы вызвать виртуальные функции из производных классов, в объявлении исключения должен быть указатель или ссылка: класса int main() { try { iStack::push() возбуждает искчение pushCnFull } catch ( const Excp seCbj ) { eCbj.print(); вызывается виртуальная функция pushCnFull::print() Объявление исключения в этом примере тоже относится к базовому классу Excp, но так как eCbj - ссылка и при этом именует объект-исключение типа pushCnFull, то для нее можно вызывать виртуальные функции, определенные в классе pushCnFull. Когда catch- обработчик обращается к виртуальной функции print(), вызывается функция из производного класса, и программа печатает следующую строку: i попытка поместить значение 879 в полный стек Таким образом, ссылка в объявлении исключения позволяет вызывать виртуальные функции, ассоциированные с классом объекта-исключения. 19.2.5. Раскрутка стека и вызов деструкторов Когда возбуждается исключение, поиск его catch-обработчика - раскрутка стека -начинается с функции, возбудившей исключение, и продолжается вверх по цепочке вложенных вызовов (см. раздел 11.3). Во время раскрутки поочередно происходят аномальные выхода: из просмотренных функций. Если функция захватила некоторый ресурс (например, открыла файл или выделила из хипа память), он в таком случае не освобождается. Существует прием, позволяющий решить эту проблему. Всякий раз, когда во время поиска обработчика происходит выход из составной инструкции или блока, где определен некоторый локальный объект, для этого объекта автоматически вызывается деструктор. (Локальные объекты рассматривались в разделе 8.1.) Например, следующий класс инкапсулирует выделение памяти для массива целых в class PTR { public: PTR() { ptr = new int[ chunk ]; } -PTR { delete[] ptr; } private: int *ptr; конструкторе и ее освобождение в деструкторе: Локальный объект такого типа создается в функции manip() перед вызовом mathFunc() : void manip( int parm ) { PTR localPtr; ... mathFunc( parm ); возбуждает исключение divideByZero ... Если miathFunc() возбуждает исключение типа divideByZero, то начинается раскрутка стека. В процессе поиска подходящего catch-обработчика проверяется и функция mianipO. Поскольку вызов miathFunc () не заключен в try-блок, то mianip() нужного обработчика не содержит. Поэтому стек раскручивается дальше по цепочке вызовов. Но перед выходом из manip() с необработанным исключением процесс раскрутки уничтожает все объекты типа классов, которые локальны в ней и были созданы до вызова mathFunc() . Таким образом, локальный объект localPtr уничтожается до того, как поиск пойдет дальше, а следовательно, память, на которую он указывает, будет освобождена и утечки не произойдет. Поэтому говорят, что процесс обработки исключений в C++ поддерживает технику программирования, основной принцип которой можно сформулировать так: захват ресурса - это инициализация; освобождение ресурса - это уничтожение . Если ресурс реализован в виде класса и, значит, действия по его захвату сосредоточены в конструкторе, а действия по освобождению - в деструкторе (как, например, в классе PTR выше), то локальный для функции объект такого класса автоматически уничтожается при выходе из функции в результате необработанного исключения. Действия, которые должны быть выполнены для освобождения ресурса, не будут пропущены при раскрутке стека, если они инкапсулированы в деструкторы, вызываемые для локальных объектов. Класс auto ptr, определенный в стандартной библиотеке (см. раздел 8.4), ведет себя почти так же, как наш класс PTR. Это средство для инкапсуляции выделения памяти в конструкторе и ее освобождения в деструкторе. Если для выделения одиночного объекта из хипа используется auto ptr, то гарантируется, что при выходе из составной инструкции или функции из-за необработанного исключения память будет освобождена. 19.2.6. Спецификации исключений С помощью спецификации исключений (см. раздел 11.4) в объявлении функции указывается множество исключений, которые она может возбуждать прямо или косвенно. Спецификация позволяет гарантировать, что функция не возбудит не перечисленные в ней исключения. Такую спецификацию разрешается задавать для функций-членов класса так же, как и для обычных функций; она должна следовать за списком параметров функции-члена. Например, в определении класса bad alloc из стандартной библиотеки C++ функции-члены имеют пустую спецификацию исключений throw() , т.е. гарантированно не возбуждают никаких исключений:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.004
При копировании материалов приветствуются ссылки. |