Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на C / C++ / C++ for real programmers.pdf
Скачиваний:
262
Добавлен:
02.05.2014
Размер:
2.04 Mб
Скачать

67

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

Вложенная обработка исключений

Да, вложение блоков try/catch разрешается, хотя пользоваться этой возможностью следует как можно реже, если только вы хотите сохранить дружеские отношения с персоналом сопровождения вашей программы.

{

try {

try {

try {

// Ненадежный фрагмент

}

catch(...) {

}

}

catch(...) {

}

}

catch(...) {

}

}

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

Внешние исключения не перехватываются!

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

Конструкторы и деструкторы

Одно из принципиальных достоинств стандартной схемы обработки исключений — раскрутка стека (unwinding the stack). При запуске исключения автоматически вызываются деструкторы всех стековых объектов между throw и catch.

void fn() throw(int) { Foo aFoo;

// Что-то не так! throw(bad_news);

}

Когда возникает исключение, до передачи стека соответствующему обработчику будет вызван деструктор aFoo. Тот же принцип действует и для try-блока вызывающей стороны.

{

try {

 

Bar b;

 

fn();

// Вызывает исключение

}

 

68

catch(int exception) {

// Перед тем, как мы попадем сюда, будет вызван деструктор b

}

}

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

Уничтожаемые объекты

Гарантируется вызов деструкторов всех стековых объектов, сконструированных с начала выполнения try-блока, но и только. Например, допустим, что к моменту возникновения исключения был сконструирован массив. Деструкторы вызываются лишь для тех объектов массива, которые были сконструированы до возникновения исключения.

Динамические объекты (то есть созданные посредством оператора new) — совсем другое дело. Вам придется самостоятельно следить за ними. Если в куче размещаются объекты, которые должны уничтожаться в результате исключения, обычно для них создается оболочка в виде вспомогательного стекового объекта.

class TempFoo { private:

Foo* f; public:

TempFoo(Foo* aFoo) : f(aFoo) {} ~TempFoo() { delete f; }

}; try {

TempFoo tf(new Foo); // и т.д.

}

catch(...) {

// Foo уничтожается деструктором tf

}

Исключения во время конструирования

Рассмотрим следующий процесс конструирования:

class Foo {...}

class Bar : public Foo { private:

Aa;

Bb;

public:

Bar();

};

Bar::Bar()

{

Xx; throw(bad_news);

Yy;

}