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

ЖАНРЫ

Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:

void f(int x)

{

try {

Vector v(x); // может генерировать исключения

// ...

}

catch (Bad_size bs) {

cerr << "Вектор неправильного размера (" << bs.sz << ")\n";

// ...

}

}

Для перехвата всех исключений можно использовать инструкцию

catch (...)
.

try {

// ...

} catch (...) { //
перехват всех исключений

// ...

}

Как правило, лучше (проще, легче, надежнее) применять технологию RAII (“Resource Acquisition Is Initialization” — “выделение ресурсов — это инициализация”), чем использовать множество явных инструкций

try
и
catch
(см. раздел 19.5).

Инструкция

throw
без аргументов (т.е.
throw;
) повторно генерирует текущее исключение. Рассмотрим пример.

try {

// ...

} catch (Some_exception& e) {

// локальная очистка

throw; // остальное сделает вызывающая функция

}

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

Когда генерируется исключение, система поддержки выполнения программ на языке С++ ищет вверх по стеку раздел

catch
, тип которого соответствует типу генерируемого объекта. Другими словами, она ищет инструкции
try
в функции, генерирующей исключение, затем в функции, вызвавшей функцию, генерирующую исключение, затем в функции, вызвавшей функцию, вызвавшей функцию, которая генерирует исключение, пока не найдет соответствие. Если соответствие найдено не будет, программа прекратит работу. В каждой функции, обнаруженной на этом пути, и в каждой области видимости, в которой проходит поиск, вызывается деструктор. Этот процесс называется раскруткой стека (stack unwinding).

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

X::~X { if (in_a_real_mess) throw Mess; } // никогда так

// не делайте!

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

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

A.15. Пространства имен

Пространство имен (namespace) объединяет связанные друг с другом объявления и предотвращает коллизию имен.

int a;

namespace Foo {

int a;

void f(int i)

{

a+= i; // это переменная a из пространства имен Foo

// (Foo::a)

}

}

void f(int);

int main

{

a = 7; // это глобальная переменная a (::a)

f(2); // это глобальная функция f (::f)

Foo::f(3); // это функция f из пространства имен Foo

::f(4); // это глобальная функция f (::f)

}

Имена можно явно уточнять именами их пространств имен (например,

Foo::f(3)
) или оператором разрешения области видимости
::
(например,
::f(2)
), который относится к глобальному пространству имен.

Все имена в пространстве имен (например, в стандартном пространстве

std
) можно сделать доступными с помощью директивы
using namespace std
;

Будьте осторожны с директивой

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

using Foo::g;

g(2); // это функция g из пространства имен Foo (Foo::g)

Более подробная информация о пространствах имен содержится в разделе 8.7.

A.16. Альтернативные имена

Для имени можно определить альтернативное имя (alias); иначе говоря, можно определить символическое имя, которое будет означать то же самое, что и имя, с которым оно связано (для большинства случаев употребления этого имени).

typedef int* Pint; // Pint — это указатель на int

namespace Long_library_name { /* ... */ }

namespace Lib = Long_library_name; // Lib — это Long_library_name

int x = 7;

int& r = x; // r — это x

Ссылки (см. разделы 8.5.5 и A.8.3) — это механизм указания на объекты, работающий на этапе выполнения программы. Ключевые слова

typedef
(см. разделы 20.5 и 27.3.1) и
namespace
относятся к механизмам ссылок на имена, работающим на этапе компиляции. В частности, инструкция
typedef
не вводит новый тип, а просто задает новое имя существующего типа. Рассмотрим пример.

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