Обработка исключительных ситуаций Исключительная ситуация (исключение) – это ошибка, возникающая во время выполнения программы. Например, ошибка работы с файлом, деление на ноль, обращение по несуществующему адресу памяти. С++ позволяет программисту возможность реагировать на исключения и обрабатывать их. Механизм обработки исключений предназначен только для событий, которые происходят во время работы самой программы и указываются явным образом.
Принцип обработки исключений Функция, которая не может выполнить требуемое действие, генерирует исключение (throw). Исключение обрабатывается где-то в контролируемом блоке (try), из которого вызывается эта функция. При этом отыскивается обработчик исключения (catch) и ему передается управление. Если обработчик не найден, то вызывается функция terminate, аварийно завершающая программу.
Контролируемый блок Контролируемый блок – код, внутри которого может произойти исключение. При этом контролируются все вложенные вызовы функций. try { // операторы } catch(тип1 имя1) { //обработка исключения 1 } … catch(тип_n имя_n) { //обработка исключения n }
Обработчик исключений Обработчик начинается со слова catch, за которым следует в скобках тип исключения. Обработчики должны располагаться непосредственно после блока try. Если указать вместо параметра многоточие (…), то обработчик будет обрабатывать исключения любых типов. После обработки исключения управление передается первому оператору, следующему за всеми обработчиками.
Порождение исключения throw выражение; Тип выражения определяет тип исключения. Обычно используются специальные классы исключений. throw без параметров используется внутри обработчика исключения для передачи исключения на более высокий уровень обработки. При поиске обработчика исключений автоматически вызываются деструкторы локальных объектов.
Пример обработки исключений #include template class stack { private: Data st[Max]; int top; public: class StackError { }; stack (): top(0) {} void push(Data x) { if (top >= Max) throw StackError(); st[top++] = x; } Data pop() { if (top
Пример обработки исключений int main() { stack s1; try { s1.push('a'); s1.push('b'); s1.push('c'); cout
Использование разных классов исключений #include template class stack { private: Data st[Max]; int top; public: class Full { }; class Empty { }; stack (): top(0) {} void push(Data x) { if (top >= Max) throw Full(); st[top++] = x; } Data pop() { if (top
Использование разных классов исключений int main() { stack s1; try { s1.push('a'); s1.push('b'); // s1.push('c'); cout
Исключения с аргументами template class stack { private: Data st[Max]; int top; public: class Error { public: char sender[20], err[20]; Error( char *s, char *t) { strcpy(sender,s); strcpy(err,t); } }; void push(Data x) { if (top >= Max) throw Error("Push","Full"); st[top++] = x; } Data pop() { if (top
Исключения с аргументами int main() { stack s1; try { s1.push('a'); s1.push('b'); s1.push('c'); cout
Встроенные исключения Стандартная библиотека C++ содержит несколько предопределенных классов исключений. Все они имеют общего предка exception. Примеры: bad_alloc – ошибка при распределении памяти; invalid_argument – непр. аргумент ф-ции и т.д.
Многофайловые программы При коллективной разработке больших программ неизбежно возникает необходимость использования нескольких исходных файлов. Обычно каждый файл содержит описание нескольких функций или классов. Рассмотрим создание библиотек классов.
Библиотека классов Библиотека состоит из интерфейса (объявления функций и классов) и реализации (тел функций и методов). Интерфейс представляет собой заголовочный файл с объявлениями с расширением.H. Реализация – откомпилированный объектный (OBJ) или библиотечный (LIB) файл.
Сборка многофайловых программ Заголовочный файл с интерфейсом включается в любой исходный файл, использующий классы, при помощи #include. Исходные и заголовочные файлы для компиляции включаются в файл проекта (BPR, DEV, DSP). В этом файле хранится информация о дате файлов, что позволяет перекомпилировать только измененные файлы.
Межфайловые переменные Переменная должна быть определена только в одном файле, в других – объявлена с помощью extern. Чтобы определить переменные с одинаковыми именами в разных файлах, используется static. //файл A int Var; static int X; //файл B extern int Var; static int X; Var = 10;
Межфайловые функции и классы. Функция определяется в одном файле, а объявляется во всех, которые ее используют. Чтобы определить функции с одинаковыми именами в разных файлах, используется static. Классы должны быть определены во всех файлах, где используются. Поэтому обычно классы определяются в заголовочных файлах и включаются во все файлы, где используются классы. Определения методов должны быть в единственном экземпляре в любых файлах, использующих заголовочный файл. Определения шаблонов функций и классов обязательно должны включаться в каждый файл, поэтому они размещаются в заголовочном файле.
Пример определения классов //stack.h class stack { private: char st[100]; int top; public: stack (): top(0) {} void push (char x); char pop(); bool empty(); };
Пример определения классов // stack.cpp #include stack.h void stack::push(char x) {st[top++] = x; } char stack:: pop() { return st[--top]; }
Ошибки повторного включения Определение функции или переменной не должно включаться в файл дважды. //headtwo.h int Var; //headone.h #include headtwo.h // app.cpp #include headone.h #include headtwo.h
Директивы препроцессора Для предупреждения повторного включения используются директивы препроцессора. #define имя // определение константы #if выражение //условная компиляция … #elif выражение … #else … #endif В выражениях можно проверить, определена ли константа с помощью defined(имя).
Пример предотвращения повторного включения //stack.h #if !defined (STACK) #define STACK class stack { private: char st[100]; int top; public: stack (): top(0) {} void push (char x); char pop(); bool empty(); }; #endif
Пространства имен В С++ для предотвращения конфликта имен функций и классов в разных файлах одного проекта можно использовать пространства имен. Пространство имен – именованная область файла. Пространство имен содержит описание переменных, функций, типов, классов.
Описание пространства имен namespace имя { … } Пример: namespace math { const double pi = ; double len (double r) { return 2 * pi * r; } }
Доступ к членам пространства имен Доступ осуществляется через область видимости cout
Неоднократное определение пространства имен Определения пространства имен могут встречаться в программе неоднократно. namespace math { const double pi = ;} … namespace math { double len (double r) { return 2 * pi * r; } } В этом случае второе определение является продолжением первого. В стандартной библиотеке C++ большинство классов описаны в пространстве имен std. Можно описать неименованное пространство имен. Оно получит имя, совпадающее с именем файла.