Чтение онлайн

ЖАНРЫ

C++. Сборник рецептов

Когсуэлл Джефф

Шрифт:

 p->foo; // Вызов версии производного класса

 p->Base::foo; // Вызов версии базового класса

}

Обсуждение

Регулярное использование переопределения полиморфных возможностей C++ является плохой идеей, но иногда это требуется сделать. Как и в случае с большинством других методик С++, это по большей части вопрос синтаксиса. Когда требуется вызвать определенную версию виртуальной функции базового класса, просто укажите ее имя после имени этого класса, как это сделано в примере 8.16.

p->Base::foo;

Здесь будет вызвана версия

foo
, определенная в
Base
, а не та, которая определена в каком-то из подклассов
Base
, на который указывает
p
.

Глава 9

Исключения

и безопасность

9.0. Введение

Данная глава содержит рецепты по обработке исключений в С++. Язык C++ обеспечивает необходимую поддержку работы с исключениями, и, используя некоторые приемы, вы сможете создавать программный код, в котором исключительные ситуации эффективно обрабатываются и легко отлаживаются.

Первый рецепт описывает семантику C++ по выбрасыванию (throwing) и перехвату (catching) исключений и затем показывает, как создавать класс для представления исключений. Это является хорошей отправной точкой, если у вас мало или совсем нет опыта работы с исключениями. Здесь описываются также стандартные классы исключений, определенные в заголовочных файлах

<stdexcept>
и
<exception>
.

Остальные рецепты иллюстрируют методы оптимального использования исключений и попутно вводят несколько важных терминов. Программное обеспечение не станет хорошим, если вы будете просто выбрасывать исключение, когда происходит что-нибудь неожиданное, или перехватывать исключение только для того, чтобы напечатать сообщение об ошибке и завершить программу аварийно. Для эффективного использования средств C++ по обработке исключений вам придется создавать программный код, который предотвращает утечку ресурсов и обеспечивает четкий режим работы при выбрасывании исключения. Эти условия известны как базовые и строгие гарантии безопасности исключений. Я описываю методы, которые позволят вам обеспечить эти гарантии для конструкторов и различных функций-членов.

9.1. Создание класса исключения

Проблема

Требуется создать свой собственный класс исключения, предназначенный для выбрасывания и перехвата исключений.

Решение

Вы можете выбрасывать (

throw
) или перехватывать (
catch
) любые типы С++, которые удовлетворяют некоторым простым требованиям, а именно имеют конструктор копирования и деструктор. Однако исключения являются сложными объектами, поэтому при проектировании класса, который представляет исключительные ситуации, необходимо рассмотреть ряд вопросов. Пример 9.1 показывает, каким может быть простой класс исключения.

Пример 9.1. Простой класс исключения

#include <iostream>

#include <string>

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
и находится тот, который перехватывает тип исключения, который был только что выброшен. Подойдет оператор примерно такого вида.

catch(Exception& е) { //...

В этом месте на основе выброшенного исключения создается новый объект

Exception
с помощью конструктора копирования класса
Exception
. (Объект исключения в области видимости
throw
является временным и может быть удален компилятором при оптимизации.) Первоначальное исключение уничтожается, поскольку осуществлен выход из диапазона его видимости и выполняется тело оператора
catch
.

Если, находясь в теле оператора

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

throw;

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

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

Exception
, содержащее указатель символьной строки или строку
string
, и затем выбрасывать его. Но такой класс не очень полезен, так как он мало чем отличается от класса-оболочки текстового сообщения. Собственно говоря, вы могли бы получить почти такой же результат, используя в качестве объекта исключения просто строку
string
.

try {

 throw(string("Something went wrong!"));

} catch (string& s) {

 cout << "The exception was: " << s << endl;

}

Нельзя сказать, что этот подход обязательно даст хорошие результаты; моя цель продемонстрировать основное свойство исключения: им может быть любой тип C++. В качестве исключений вы можете выбрасывать тип

int
,
char
,
class
,
struct
или любой другой тип C++, если действительно это потребуется. Но вам лучше использовать иерархию классов исключений, находящихся либо в стандартной библиотеке, либо ваших собственных.

Поделиться с друзьями: