Шаг 121.
Библиотека STL.
Ошибки и исключения внутри STL. Обработка исключений

    На этом шаге мы рассмотрим особенности обработки исключений.

    Проверка логических ошибок в STL практически отсутствует, поэтому сама библиотека STL почти не генерирует исключения, связанные с логикой. Фактически существует только одна функция, для которой в стандарте прямо указано на возможность возникновения исключения: речь идет о функции at() векторов и деков (проверяемой версии оператора индексирования). Во всех остальных случаях стандарт требует лишь стандартных исключений типа bad_alloc при нехватке памяти или исключений, возникающих при пользовательских операциях. Когда генерируются исключения и что при этом происходит с компонентами STL? В течение долгого времени, пока шел процесс стандартизации, строгих правил на этот счет не существовало. В сущности, любое исключение приводило к непредсказуемым последствиям. Даже уничтожение контейнера STL после того, как во время выполнения одной из поддерживаемых им операций произошло исключение, тоже приводило к непредсказуемым последствиям (например, аварийному завершению программы). Из-за этого библиотека STL оказывалась бесполезной там, где требовалось гарантированное четко определенное поведение, потому что не была предусмотрена даже возможность раскрутки стека.

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

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

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

    Если вам понадобится контейнер с полными гарантиями транзакционной безопасности, используйте либо список (без вызова функций sort() и unique()), либо ассоциативный контейнер (без вызова многоэлементных операций вставки). Тогда вам не придется создавать копии данных перед модификацией, чтобы предотвратить возможную потерю данных. Копирование контейнеров иногда обходится очень дорого.

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

template <class Т, class Cont, class Iter>
void insert (Cont& coll, const Iter& pos, const T& value)
{
  Cont tmp(coll);         // Копирование контейнера со всеми элементами
  tmp.insert(pos,value);  // Модификация копии
  coll.swap(tmp);         // Использование копии (если 
                          // модификация выполнена без исключений)
} 

    "Почти" означает, что эта функция не идеальна. Дело в том, что функция swap() тоже может генерировать исключение, если оно имеет место в критерии сравнения ассоциативного контейнера. Как видите, безупречная обработка исключений - дело весьма непростое.

    На следующем шаге мы рассмотрим расширение STL.




Предыдущий шаг Содержание Следующий шаг