Программирование >>  Синтаксис инициирования исключений 

1 ... 14 15 16 [ 17 ] 18 19 20 ... 82


Исключения


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

Базовый принцип, на котором основана обработка исключений, - восстановление состояния и выбор альтернативных действий в случае ошибки. Предположим, в вашей программе имеется некий блок и вы не уверены, что он доработает до конца. При выполнении блока может возникнуть нехватка памяти, или начнутся проблемы с коммуникациями, или нехороший клиентский объект передаст неверный параметр. Разве не хотелось бы написать программу в таком виде:

if ( блок будет работать) { блок;

else {

сделать что-то другое;

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

Обработка исключений в стандарте ANSI

Хорошая новость: для обработки исключений существует стандарт ANSI - или, как это всегда бывает в C++, предложенный стандарт. Интересно, почему у нас так много предложенных стандартов и ни одного реального? Скорее всего, дело в том, что наша экономика не может вместить всех безработных членов комитетов стандартизации. Лучше оставить им занимательное пожизненное хобби, пока мы будем выполнять свою работу. Впрочем, я отвлекся.

Плохая новость: стандартная обработка исключений все еще не поддерживается многими компиляторами C++. Хорошая новость: все больше и больше компиляторов выходит на передовые позиции. Плохая новость: осталось немало старого кода, предназначенного для старых компиляторов.

Увы.

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

Синтаксис инициирования исключений

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

enum Gotcha { kTooLow, kTooHigh }; void fn(int x) throw(Gotcha) {



if (x < 0)

throw kTooLow; Функция завершается здесь if (x > 1000)

throw kTooHigh; Или здесь Сделать что-то осмысленное

В первой строке определяется тип исключения. Исключения могут иметь любой тип: целое, перечисление, структура и даже класс. Во второй строке объявляется интерфейс функции с новым придатком - спецификацией исключений, который определяет, какие исключения могут быть получены от функции вызывающей стороной. В данном примере инициируется исключение единственного типа Gotcha. В четвертой и шестой строке показано, как инициируются исключения, которые должны быть экземплярами одного из типов, указанного в спецификации исключений данной функции. Спецификации исключений должны подчиняться следующим правилам.

Объявления и определения

Спецификация исключений в объявлении функции должна точно совпадать со спецификацией в ее определении.

void Fn() throw(int); Объявление Где-то в файле .cpp void Fn() throw(int) { Реализация

Если определение будет отличаться от объявления, компилятор скрестит руки на груди и откажется компилировать определение.

Функции без спецификации исключений

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

void fn(); Может инициировать исключения любого типа Функции, не инициирующие исключений

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

void fn() throw(); Не инициирует исключений Функции, инициирующие исключения нескольких типов

В скобках можно указать произвольное количество типов исключений, разделив их запятыми. void fn() throw(int, Exception Struct, char*);

Передача исключений

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

void fn() throw; Исключения и сигнатуры функций

Спецификация исключений не считается частью сигнатуры функции. Другими словами, нельзя иметь две функции с совпадающим интерфейсом за исключением (нечаянный каламбур!) спецификации исключений. Две следующие функции не могут сосуществовать в программе:



void f1(int) throw();

void f1(int) throw(Exception); Повторяющаяся сигнатура!

Спецификация исключений для виртуальных функций

В главе 2 мы говорили (точнее, я говорил, а вы слушали) об отличиях между перегрузкой (overloading) и переопределением (overriding). Если виртуальная функция в производном классе объявляется с новой сигнатурой, отсутствующей в базовом классе, эта функция скрывает все одноименные функции базового класса (если вы в чем-то не уверены, вернитесь к соответствующему разделу; это важно понимать). Аналогичный принцип действует и для спецификаций исключений.

class Foo { public:

virtual Fn() throw(int);

class Bar : public Foo { public:

virtual Fn() throw(char*); Осторожно!

Компилятор косо посмотрит на вас, но откомпилирует. В результате тот, кто имеет дело с Foo*, будет ожидать исключения типа int, не зная, что на самом деле он имеет дело с объектом Ваr, инициирующим нечто совершенно иное.

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

Непредусмотренные исключения

Если инициированное исключение отсутствует в спецификации исключений внешней функции, программа переформатирует ваш жесткий диск. Шутка. На самом деле она вызывает функцию с именем unexpected() . По умолчанию затем вызывается функция terminate() , о которой будет рассказано ниже, но вы можете сделать так, чтобы вызывалась ваша собственная функция. Соответствующие интерфейсы из заголовочного файла except.h выглядят так:

typedef void (*unexpected function)();

unexpected function set unexpected(unexpected function excpected func);

В строке typedef.. . объявляется интерфейс к вашей функции. Функция set unexpected() получает функцию этого типа и организует ее вызов вместо функции по умолчанию. Функция set unexpected() возвращает текущий обработчик непредусмотренных исключений. Это позволяет временно установить свой обработчик таких исключений, а потом восстановить прежний. В следующем фрагменте показано, как используется этот прием.

unexpected function my hand1er(void) { Обработать неожиданное исключение

{ Готовимся сделать нечто страшное и устанавливаем свой обработчик unexpected function o1d hand1er = set unexpected(my hand1er); Делаем страшное и возвращаем старый обработчик set unexpected(o1d hand1er);

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



1 ... 14 15 16 [ 17 ] 18 19 20 ... 82

© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика