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

  typename Vec::value_type xx = x; // здесь нужно слово

                                      // "typename"

  // ...

}

Более подробная информация о шаблонах приведена в главе 19.

<p id="AutBody_Root630"><strong>A.14. Исключения</strong></p>

Исключения используются (посредством инструкции throw) для того, чтобы сообщить вызывающей функции об ошибке, которую невозможно обработать на месте. Например, спровоцируем исключение Bad_size в классе Vector.

struct Bad_size {

  int sz;

  Bad_size(int s):ss(s) { }

};

class Vector {

  Vector(int s) { if (s<0 || maxsize

  // ...

};

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

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; } // никогда так

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

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

<p id="AutBody_Root631"><strong>A.15. Пространства имен</strong></p>

Пространство имен (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

Перейти на страницу:

Похожие книги

97 этюдов для архитекторов программных систем
97 этюдов для архитекторов программных систем

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

Билл де Ора , Майкл Хайгард , Нил Форд

Программирование, программы, базы данных / Базы данных / Программирование / Книги по IT