Бьерн Страуструп - Язык программирования С++. Главы 9-10
Страница 2. Обработка ошибок



9.1 Обработка ошибок

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

 Ь Только недавно комитетом по стандартизации С++ особые ситуации были
 включены в стандарт языка, но на время написания этой книги они еще
 не вошли в большинство реализаций.

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

               class Vector {
                 int* p;
                 int  sz;
               public:
                 class Range  {  };  // класс для особой ситуации

                 int& operator[](int i);

                 // ...
               };

  Предполагается, что объекты класса Range будут использоваться как
  особые ситуации, и запускать их можно так:

               int& Vector::operator[](int i)
               {
                 if (0<=i && i<sz) return p[i];
                 throw Range();
               }

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

           void f(Vector& v)
           {
            // ...

            try {
             do_something(v);  // содержательная часть, работающая с v
            }
            catch (Vector::Range) {
            // обработчик особой ситуации Vector::Range

            // если do_something() завершится неудачно,
            // нужно как-то среагировать на это

            // сюда мы попадем только в том случае, когда
            // вызов do_something() приведет к вызову Vector::operator[]()
            // из-за недопустимого значения индекса

            }

            // ...
          }

Обработчиком особой ситуации называется конструкция

           catch ( /* ... */ ) {
             // ...
           }

 Ее можно использовать только сразу после блока, начинающегося служебным
 словом try, или сразу после другого обработчика особой ситуации. Служебным
 является и слово catch. После него идет в скобках описание, которое
 используется аналогично описанию формальных параметров функции, а именно,
 в нем задается тип объектов, на которые рассчитан обработчик, и,
 возможно, имена параметров (см. $$9.3). Если в do_something() или в
 любой вызванной из нее функции произойдет ошибка индекса (на любом
 объекте Vector), то обработчик перехватит особую ситуацию и будет
 выполняться часть, обрабатывающая ошибку. Например, определения следующих
 функций приведут к запуску обработчика в f():

          void do_something()
          {
            // ...
            crash(v);
            // ...
          }

          void crash(Vector& v)
          {
             v[v.size()+10];  // искусственно вызываем ошибку индекса
          }

    Процесс запуска и перехвата особой ситуации предполагает просмотр
 цепочки вызовов от точки запуска особой ситуации до функции, в которой
 она перехватывается. При этом восстанавливается состояние стека,
 соответствующее функции, перехватившей ошибку, и при проходе по всей
 цепочке вызовов для локальных объектов функций из этой цепочки вызываются
 деструкторы. Подробно это описано в $$9.4.
    Если при просмотре всей цепочки вызовов, начиная с запустившей
 особую ситуацию функции, не обнаружится подходящий обработчик, то
 программа завершается. Подробно это описано в $$9.7.
    Если обработчик перехватил особую ситуацию, то она будет обрабатываться
 и другие, рассчитанные на эту ситуацию, обработчики не будут
 рассматриваться. Иными словами, активирован будет только тот обработчик,
 который находится в самой последней вызывавшейся функции, содержащей
 соответствующие обработчики. В нашем примере функция f() перехватит
 Vector::Range, поэтому эту особую ситуацию нельзя перехватить ни в
 какой вызывающей f() функции:

          int ff(Vector& v)
          {
            try {
                  f(v);         // в f() будет перехвачена Vector::Range
            }
            catch (Vector::Range) { // значит сюда мы никогда не попадем
               // ...
            }
          }

 
« Предыдущая статья   Следующая статья »