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

   
На этом шаге мы рассмотрим обработку ошибок в STL.

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

   
При проектировании STL главным приоритетом была максимальная производительность, а не безопасность. Проверка ошибок
требует времени, поэтому в STL она практически не выполняется. Если вы умеете программировать без ошибок, все
замечательно, а если нет - дело кончится катастрофой. Перед включением библиотеки STL в стандартную библиотеку C++
обсуждался вопрос о том, не нужно ли расширить обработку ошибок. Большинство высказались против по двум причинам.

  • Обработка ошибок снижает быстродействие, а высокая скорость работы попрежнему остается основной целью большинства
    программ. Как упоминалось выше, быстродействие было одним из приоритетов при проектировании STL.
  • Тот, кто ставит на первое место надежность, может добиться своего при помощи интерфейсных оболочек или специальных
    версий STL. С другой стороны, невозможно повысить быстродействие за счет отказа от проверки ошибок, если эта проверка
    включена во все базовые операции. Например, если при любой выборке элемента по индексу проверяется, принадлежит ли индекс к
    интервалу допустимых значений, вам не удастся написать собственную процедуру индексации без проверки. А вот обратная ситуация
    вполне возможна.

   
В результате проверка ошибок в STL возможна, но не обязательна.

   
В спецификации стандартной библиотеки C++ указано, что любое использование STL, нарушающее предварительные
условия, приводит к непредсказуемому поведению. Следовательно, при недействительном индексе, итераторе или
интервале может произойти все что угодно. Если не использовать безопасную версию STL, обычно дело кончается
нарушением защиты памяти с неприятными побочными эффектами и даже сбоем программы. В некотором смысле применение
библиотеки STL так же чревато ошибками, как применение указателей в языке С. Найти такие ошибки иногда бывает
очень трудно, особенно если нет безопасной версии STL.

   
В частности, для нормальной работы STL должны выполняться перечисленные ниже условия.

  • Действительность итераторов. Например, перед использованием итераторы должны инициализироваться. Следует помнить,
    что итераторы могут стать недействительными вследствие побочных эффектов других операций. В частности, в векторах и деках это
    может произойти при вставке, удалении или перемещении элементов.
  • Конечный итератор не связан с элементом контейнера, поэтому вызов операторов * и -> для него недопустим. В частности, это
    относится к возвращаемым значениям функций end() и rend() контейнерных классов.
  • Действительность интервалов:
    • итераторы, определяющие интервал, должны относиться к одному контейнеру;
    • второй итератор должен быть достижим в результате перебора элементов, начиная от первого.
  • При использовании нескольких интервалов-источников второй и последующие интервалы должны содержать не меньше элементов,
    чем первый интервал.
  • Количество элементов в приемном интервале должно быть достаточным для перезаписи; в противном случае необходимо
    использовать итераторы вставки. Некоторые распространенные ошибки продемонстрированы в следующем примере:
//---------------------------------------------------------------------------

#include <vcl.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <conio.h> //необходимо для getch()

#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused
using namespace std;

std::string ToRus(const std::string &in)
{
  char *buff = new char [in.length()+1];
  CharToOem(in.c_str(),buff);
  std::string out(buff);
  delete [] buff;
  return out;
}

template <class T>
inline void PRINT_ELEMENTS (const T& coll, const char* optcstr="")
{
  typename T::const_iterator pos;
  std::cout << ToRus(optcstr);
  for (pos=coll.begin(); pos!=coll.end(); ++pos) {
    std::cout <<*pos <<' ';
  }
  std::cout << std::endl;
}

int main(int argc, char* argv[])
{
  vector<int> coll1;  // Пустая коллекция
  vector<int> coll2;  // Пустая коллекция
  // ОШИБКА: начало находится за концом интервала

  vector<int>::iterator pos = coll1.begin(); 
  reverse (++pos,coll1.end());
  // Вставка элементов со значениями от 1 до 9 в coll2 
  for (int i=1; i<=9; ++i) {
    coll2.push_back(i);
  }


  PRINT_ELEMENTS(coll2,"Инициализация coll2:\n");

  // ОШИБКА: перезапись несуществующих элементов
  copy (coll2.begin(), coll2.end(),  // Источник
        coll1.begin());     //Приемник

  PRINT_ELEMENTS(coll1,"Значения coll1:\n");

  // ОШИБКА:
  // - перепутаны коллекции
  // - перепутаны begin() и end()
  copy (coll1.begin(), coll2.end(),     // Источник
        coll1.end());

  PRINT_ELEMENTS(coll1,"Значения coll1:\n");

  cout << endl;

  getch();
  return 0;
}
//---------------------------------------------------------------------------

Текст этого примера можно взять http://www.horstmann.com/safestl.html.

   
К сожалению, многие поставщики предоставляют версию STL, основанную на исходном варианте кода, в котором обработка
ошибок не предусмотрена. Впрочем, ситуация постепенно улучшается. Современная безопасная версия STL распространяется
бесплатно практически для всех платформ (http://www.stlport.org/).

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



Вы можете оставить комментарий, или Трекбэк с вашего сайта.

Оставить комментарий