Глава 9
Исключения и безопасность
Данная глава содержит рецепты по обработке исключений в С++. Язык C++ обеспечивает необходимую поддержку работы с исключениями, и, используя некоторые приемы, вы сможете создавать программный код, в котором исключительные ситуации эффективно обрабатываются и легко отлаживаются.
Первый рецепт описывает семантику C++ по выбрасыванию (throwing) и перехвату (catching) исключений и затем показывает, как создавать класс для представления исключений. Это является хорошей отправной точкой, если у вас мало или совсем нет опыта работы с исключениями. Здесь описываются также стандартные классы исключений, определенные в заголовочных файлах и .
Остальные рецепты иллюстрируют методы оптимального использования исключений и попутно вводят несколько важных терминов. Программное обеспечение не станет хорошим, если вы будете просто выбрасывать исключение, когда происходит что-нибудь неожиданное, или перехватывать исключение только для того, чтобы напечатать сообщение об ошибке и завершить программу аварийно. Для эффективного использования средств C++ по обработке исключений вам придется создавать программный код, который предотвращает утечку ресурсов и обеспечивает четкий режим работы при выбрасывании исключения. Эти условия известны как базовые и строгие гарантии безопасности исключений. Я описываю методы, которые позволят вам обеспечить эти гарантии для конструкторов и различных функций-членов.
9.1. Создание класса исключения
Проблема
Требуется создать свой собственный класс исключения, предназначенный для выбрасывания и перехвата исключений.
Решение
Вы можете выбрасывать ( throw) или перехватывать ( catch) любые типы С++, которые удовлетворяют некоторым простым требованиям, а именно имеют конструктор копирования и деструктор. Однако исключения являются сложными объектами, поэтому при проектировании класса, который представляет исключительные ситуации, необходимо рассмотреть ряд вопросов. Пример 9.1 показывает, каким может быть простой класс исключения.
Пример 9.1. Простой класс исключения
#include
#include
using namespace std;
class Exception {
public:
Exception(const string& msg) : msg_(msg) {}
~Exception() {}
string getMessage() const {return(msg_);}
private:
string msg_;
};
void f() {
throw(Exception("Mr. Sulu"));
}
int main() {
try {
f();
} catch(Exception& e) {
cout << "You threw an exception: " << e.getMessage() << endl;
}
}
Обсуждение
В языке C++ поддержка исключений обеспечивается при помощи трех ключевых слов: try, catchи throw. Они имеют следующий синтаксис.
try {
// Что-нибудь, что может вызвать функцию "throw", например:
throw(Exception("Uh-oh"));
} catch(Exception& e) {
// Какие-нибудь полезные действия с объектом исключения е
}
Исключение в C++ (аналогично в Java и С#) - это способ, позволяющий поместить сообщение в бутылку, выбросить ее за борт и надеяться, что кто-нибудь пытается найти ваше сообщение где-нибудь ниже по стеку вызовов. Это является альтернативой другим, более простым методам, когда, например, возвращается код ошибки или выдается сообщение об ошибке. Семантика использования исключений (например, «попытка выполнения» каких-то действий, «выбрасывание» исключения с последующим его «перехватом») отличается от других операций С++, поэтому перед описанием способа создания класса исключения я кратко отвечу на вопрос, что представляет собой исключение и что значит выбросить и перехватить его.
Когда возникает исключительная ситуация и вы полагаете, что вызывающая программа должна быть осведомлена об этом, можете «поместить ваше сообщение в бутылку» в операторе throw, как показано ниже.
throw(Exception("Something went wrong"));
В результате среда времени выполнения сконструирует объект Exception(исключение), и затем начинается раскрутка стека вызовов до тех пор, пока не найдется блок try, в который был сделан вход, но из которого еще не сделан выход. Если среда времени выполнения не найдет такой блок, т.е. достигнет функции main(или верхний уровень текущего потока вычислений), и не может дальше раскручивать стек вызовов, вызывается специальная глобальная функция terminate. Но если блок tryнайден, то просматривается каждый оператор catchдля данного блока tryи находится тот, который перехватывает тип исключения, который был только что выброшен. Подойдет оператор примерно такого вида.
Читать дальше