|
Программирование >> Разработка устойчивых систем
int Trace::counter = 0: int mainO { try { Trace nl: Запуск исключения: Trace array[5]: Trace n2: Сюда не попадаем } catch(int i) { cout caught i endl: } /:- используется секция catch(...), которая вроде бы должна перехватить все исключения, не оставляя повода для вызова terminate(), функция terminate() все равно будет вызвана. В процессе уничтожения объектов в стеке при обработке первого исключения вызывается деструктор Botch, который генерирует второе исключение и становится причиной вызова terminateQ. Следовательно, деструктор, который сам запускает исключения или приводит к их запуску, обычно является признаком плохого проектирования или небрежного программирования. Зачистка Одно из преимуществ обработки исключений состоит в том, что нормальный ход программы прерывается, и управление сразу передается в соответствующий обработчик исключений. Но эта передача принесет пользу только в том случае, если в момент запуска исключения будет проведена необходимая деинициализация. Механизм обработки исключений С++ гарантирует, что при выходе из области видимости для всех объектов этой области, конструкторы которых завершены, будут вызваны деструкторы. Следующий пример убеждает в том, что для объектов с незаверщенными конструкторами деструкторы не вызываются. Кроме того, он показывает, что происходит при запуске исключения в процессе создания массива объектов: : С01:С1еапир.срр При запуске исключения уничтожаются только готовые объекты #1 nclude <iostream> using namespace std: class Trace { static int counter: int objid: public: TraceO { objid = counter++: cout constructing Trace # objid endl: if(objid == 3) throw 3: -TraceO { cout destructing Trace # objid endl: Класс Trace выводит информацию о создании и уничтожении своих объектов, что позволяет проследить за ходом выполнения программы. Класс подсчитывает созданные объекты в статической переменной counter, а идентификатор конкретного объекта хранится в переменной objid. Функция main() создает один объект п1 (objid 0), а затем пытается создать массив из пяти объектов Trace, но создание четвертого объекта (идентификатор 3) прерывается. Таким образом, создание объекта п2 так и не завершается. Из выходных данных программы видно, как происходит вызов деструкторов: constructing Trace #0 constructing Trace #1 constructing Trace #2 constructing Trace #3 destructing Trace #2 destructing Trace #1 destructing Trace #0 caught 3 Три элемента массива создаются успешно, но в процессе вызова конструктора четвертого элемента запускается исключение. Поскольку в main() конструирование четвертого объекта (array[2]) не было завершено, в программе вызываются деструкторы только для объектов аггау[1] и аггау[0]. В конце уничтожается объект п1, но не объект п2, который так и не был создан. Управление ресурсами Программируя обработку исключений, всегда следует задавать себе вопрос: Если произойдет исю1ючение, будут ли освобождены задействованные ресурсы? Как правило, механизм освобождения ресурсов работает достаточно надежно, но существует специфическая проблема, связанная с конструкторами: если до завершения конструктора будет сгенерировано исключение, то для данного объекта деструктор не вызывается. Это означает, что при написании конструкторов необходимо быть особенно внимательным. Проблема связана с выделением ресурсов в конструкторах. Если в конструкторе произойдет исключение, то деструктор не сможет освободить этот ресурс. Чаще всего эта проблема проявляется в виде зависших указателей. Пример: : C01:Rawp.cpp Зависшие указатели linclude <iostream> linclude <cstddef> using namespace std; class Cat { public: CatO { cout CatO endl; } -CatO { cout -CatO endl: } class Dog { public: void* operator new(size t sz) { cout allocating a Dog endl; throw 47: void operator delete(void* p) { cout deallocating а Dog endl ::operator delete(p): class UseResources { Cat* bp: Dog* op: public: UseResources(int count = 1) { cout UseResourcesO endl: bp = new Cat[count]: op = new Dog; -UseResourcesO { cout -UseResourcesO endl: delete [] bp: Уничтожение массива delete op: int mainO { try { UseResources ur(3): } catchOnt) { cout inside handler endl: } III:-Результат: UseResourcesO Cat О Cat О Cat О allocating a Dog inside handler Программа входит в конструктор UseResources, и конструктор Cat успешно завершается для трех объектов массива. Однако при вызове Dog::operator new происходит исключение (имитация нехватки памяти). Внезапно управление передается в обработчик без вызова деструктора UseResources - и это логично, потому что конструктор UseResources не завершился. Но это также означает, что объекты Cat, успешно созданные в куче, тоже не будут уничтожены. Управление ресурсами на уровне объектов Чтобы предотвратить подобную утечку ресурсов, необходимо отказаться от низкоуровневого выделения ресурсов. Существуют два возможных способа: перехват исключений внутри конструктора с последующим освобождением ресурса; выделение ресурсов только в конструкторе объекта, чтобы освобождение ресурсов могло выполняться внутри деструктора. Во втором варианте каждая операция выделения ресурсов становится атомарной вследствие того, что эти операции являются частью жизненного цикла локального объекта. Если выделение ресурса завершается неудачей, другие объекты вы-
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.062
При копировании материалов приветствуются ссылки. |