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) { // значит сюда мы никогда не попадем
                // ...
             }
           }

Назад  |  Вперёд


OtDiatlovaOU
К началу книги

Оформление и дизайн книги OtDiatlovaOU.
Вся книга, архив.