ООП. Пособие по ООП в С++. Создание сложных программных систем Объектно ориентированная технология
Скачать 2.1 Mb.
|
Try-блок может содержать любую инструкцию языка C++: как выражения, так и объявления. Он вводит локальную область видимости, так что объявленные внутри него переменные недоступны вне этого блока, в том числе и в catch-обработчиках. Исключение может генерироваться не в try блоке, а в функциях, прямо или косвенно включенных в блок try. Не всегда исключение, возникшее во внутреннем блоке, может быть сразу правильно обработано. В этом случае допускаются вложенные контролируемые блоки, и исключение передается на более высокий уровень с помощью throw без параметров. 2. Порождение (генерация) исключения происходит по ключевому слову throw. throw[операнд ]; Выражение throw используется только в программных исключениях и означает, что исключительное условие произошло в блоке try. В качестве операнда выражения throw можно использовать объект любого типа. Обычно этот объект используется для передачи информации об ошибке. Тип операнда определяет тип порождаемого исключения. При генерации исключения прекращается выполнение текущего кода и начинается поиск соответствующего обработчика и передача ему управления. Обработчики исключений Располагаются сразу за контролируемым блоком. Можно записывать один или более обработчиков с соответствующими типами обрабатываемых исключений. catсh (тип имя параметра) { тело обработчика } – параметр принимает передаваемое значение, которое можно обработать в теле обработчика. catсh (тип) { тело обработчика } – обработчик без параметра catсh (…) { тело обработчика } – может перехватить любое исключение. тип – определяет тип исключения, может быть представлен T или T& или T* или constT или constT&. Т – любой тип языка С++, включая класс. Для С программ операнд может быть целого типа без знака. имя – используется в теле обработчика, например, для вывода сообщения об исключении. Если используется catсh (…) c многоточием, то этот обработчик перехватывает все исключения. Так как обработчики просматриваются последовательно, обработчик catсh(…) должен быть последним в последовательности. Обработчик считается найденным, если тип объекта, указанного после throw T или T& или T* const T const T& • является производным от указанного в catсh (тип объекта) типа, если наследование по public • указателем, который может быть преобразован по стандартным правилам к типу в catсh (тип) Выход из секции обработчика выполняется одним из следующих способов: • выполняются все операторы обработчика и происходит неявный переход к оператору, расположенному после конструкции try...catch; • в обработчике выполняется оператор goto , при этом разрешается переходить на любой оператор вне конструкции try...catch, а внутрь контролируемого блока или в другую секцию-ловушку переход запрещен; • в обработчике выполняется оператор return, после чего происходит нормальный выход из функции; • в секции выполняется оператор throw; • в обработчике генерируется другое исключение. Рассмотрим подробнее два последние способа выхода. Оператор throw без выражения генерации исключения генерирует повторное исключение того же типа. Такая форма оператора допустима только внутри секции-ловушки. Завершение обработки. После обработки исключения управление передается на оператор, следующий за обработчиками исключений. Примеры формирования и обработки исключений 1) перехват возникшего аппаратного исключения Чтобы перехватить (без генерации) исключение в VC++ надо выполнить настройку компиляции: Проект→Свойства→C++→Генерация кода→(в правом окне) Включить С++ исключения→Да, с SEH исключениями (/EHa) try { string s= "Ошибка" ; int a[3]={1,2,3}; int x=5, y=0,z=0; z=x/y; } catch (...){ cout<< "error" < 2) генерация исключения целого типа int main() { try { int x=5, y=0,z=0; if (y==0) throw 1; z=x/y; } catch ( int s){ cout< 0; } 3) генерация исключения строкового типа try { string s= "Ошибка" ; int x=5, y=0,z=0; if (y==0) throw s; z=x/y; } catch (string s){ cout< 12.1 Класс exception Все исключения в языке C++ описываются абстрактным классом exception, который определен в заголовочном файле Пример. Генерация исключения типа exception в методе при проверке знаменателя дроби на 0. #include #include Droby { private: int a,b; int Nod(); //реализация вне класса public: Droby( int a1, int b1); Droby(){a=0;b=0;} void Add(Droby &d); void set_a( int a1){a=a1;} //генерация исключения в методе void set_b( int b1){ if (!b1) throw std::exception(); b=b1; } int get_a(){ return a;} int get_b(){ return b;} Droby& Sub(Droby &d); }; //реализация объявленных методов Droby::Droby( int a1, int b1) { this ->set_b(b1); a=a1; b=b1; int N=Nod();a= a/N; b= b/N; } int main() { try { Droby D(2,0); } catch ( const std::exception& err) { std::cout << "Error!!!" << std::endl; } return 0; } Или можно обработать так catch (std::exception err) { std::cout << err.what() << std::endl; } err.what() – вернет переданное сообщение. Пример применения исключений exception try { int x=5, y=0; if (y==0) throw exception( "Деление на нуль" );//создание и выброс объекта z=x/y; } catch (range_error s){ cout<< "range_error" < (domain_error s){ cout<< "domain_error" < (exception s){ cout< Результат Деление на нуль 12.2 Применение try в функции Блок try может быть локализован в какой-то функции. В таком случае всякий раз при входе в функцию начинается обработка исключений. После каждого исключения функция возвращает управление в функцию main. При каждом новом вызове функции возвращается обработка исключений. Важно понимать, что код, ассоциированный с инструкцией catch, будет исполняться только тогда, когда перехвачено исключение. В противном случае выполнение программы просто обойдет инструкцию catch. Пример. Организация обработки исключений в функции #include { int x=512; int y=0; if (y==0) throw "divide 0" ; x=x/y; } void f1( int a, int b) { if (b==0) throw 0; } void f2() { f1(4,0); } int main() { try { f2(); cout<< "Close" << '\n' ;; } catch ( char *s) { std::cout<< "f2=" < ; } catch ( int i) { std::cout<< "f1=" <'\n' ; } catch (...) { std::cout<< "f3=" << '\n' ;; std::cout<< "f4=" << '\n' ;; } try { f3(); std::cout<< "Close" << '\n' ;; } catch ( char *s) { std::cout<< "f2=" < ; } catch ( int i) { std::cout<< "f1=" <} catch (...) { std::cout<< "f3=" << '\n' ;; std::cout<< "f4=" << '\n' ;; } std::cout<< "Close" << '\n' ;; std::cin.get(); return 0; } 12.3 Типы исключений Кроме типа exception в C++ есть еще несколько производных типов исключений, которые могут использоваться при различных ситуациях. Представлены на рисунке Пример применения типа #include #include #include int , int ); int main() { int a = 500; int b = 0; try { double c = divide(a, b); std::cout << c << std::endl; } catch (std::runtime_error err) { std::cout << "Runtime_error: " << err.what() << std::endl; } catch (std::exception err) { std::cout << "Exception!!!" << std::endl; } std::cout << "The End..." << std::endl; return 0; } double divide( int a, int b) { if (b == 0) throw std::runtime_error( "Division by zero!" ); return a / b; } Результат Рис. 14. Типы исключений Большинство этих типов определено в заголовочном файле stdexcept, за исключением класса bad_alloc, который определн в файле new, и класса bad_cast, который определен в файле type_info. В отличие от классов exception, bad_alloc и bad_cast в конструкторы других типов можно передать строку, то есть таким образом можно передать сообщение об ошибке. • runtime_error: общий тип исключений, которые возникают во время выполнения • range_error: исключение, которое возникает, когда полученный результат превосходит допустимый диапазон • overflow_error: исключение, которое возникает, если полученный результат превышает допустимый диапазон • underflow_error: исключение, которое возникает, если полученный в вычислениях результат имеет недопустимые отрицательное значение (выход за нижнюю допустимую границу значений) • logic_error: исключение, которое возникает при наличии логических ошбок к коде программы • domain_error: исключение, которое возникает, если для некоторого значения, передаваемого в функцию, не определено результата • invalid_argument: исключение, которое возникает при передаче в функцию некорректного аргумента • length_error: исключение, которое возникает при попытке создать объект большего размера, чем допустим для данного типа • out_of_range: исключение, которое возникает при попытке доступа к элементам вне допустимого диапазона. Пример использования исключения invalid_argument #include #include #include { if (c > numeric_limits //.код функции с использованием аргумента.. } int main() { try { MyFunc(256); //будет выброшено исключение } catch (invalid_argument& e) { cerr << e.what() << endl; return -1; } //... return 0; } Пример проверки ввода числовых данных и перехват исключения деления на нуль int _tmain( int argc, _TCHAR* argv[]) { setlocale(LC_ALL, "rus" ); try { string s= "Ошибка" ; int a[3]={1,2,3}; int kodch; int x, y,z; //cout<<"Введите делимое"< ((strcmp(ss, "" ))) //если выполнено преобразование, т.е.*ss пусто { throw exception(); } //cout<<"Введите делитель"< ((strcmp(ss, "" ))) //если выполнено преобразование, т.е.*ss пусто { throw exception(); } z=x/y; } catch (exception s){ cout<< "Введено не число" < (...){ cout<< "Деление на нуль" < 0; } Пример генерации исключения методом класса class Droby { private : int a,b; int Nod(); public : Droby( int a1, int b1); Droby(){a=0;b=0;} / void Add(Droby &d); void set_a( int a1){a=a1;} //генерация исключения методом void set_b( int b1){ if (!b1) throw std::exception();b=b1; } int get_a(){ return a;} int get_b(){ return b;} Droby& Sub(Droby &d); }; int main() { try { Droby D(2,0); } catch ( const std::exception& err) { std::cout << "Error!!!" << std::endl; } return 0;} 12.4 Вложенные исключения Формат вложенных блоков исключений try {// блок, который может инициировать исключения try {//вложенный блок ……………. } catch(...){ …………. } } catch (исключение) {// обработка исключения try {//вложенный блок …………….. } catch(...){ ………………… } } 12.5 Исключения в конструкторе и деструкторе. Собственные классы исключений В С++ конструктор и деструктор не могут возвращать результат. Механизм исключенний позволяет передать сообщение, возникшее в конструкторе или деструкторе объекта. В обработчике исключения можно использовать стандарные способы выдачи сообщений об ошибке. В классе исключения можно хранить информацию об исключении, которая может передаваться обработчику. Т.е. информация об ошибке передается из точки ее обнаружения в то место программы, где для обработки ошибки имеется достаточно возможностей. При генерации исключения в конструкторе, деструктор будет вызван автоматически для полностью созданных в этом блоке к текущему моменту объектов, а так же для полей данных текущего объекта, являющихся объектами и для его базовых классов. Например, для массива объектов деструкторы будут вызваны только для полностью созданных объектов. Если в конструкторе создается динамическая структура и возникает исключение, то память динамической структуры освобождается. Пример класса с конструктором и собственным классом исключения #include "iostream" #include { private: int *a; public: class Size / /класс исключений { public : Size(){}; string get(){ return "Exeption" ;} }; int n; enum E{max=2};//максимальный размер массива massiv( int n1) { if (n1<0||n1>max) throw Size(); n=n1; a=new int [n]; } }; int main() { try { massiv a(3); cout< (massiv::Size S) { cout< } 12.6 ExceptionZeroDivide'>Создание собственных классов-исключений дочерних классу std::exception Пример создания собственного класса исключения ExceptionZeroDivide для контроля деления на нуль class ExceptionZeroDivide { }; class Droby { private : int a,b; int Nod(); public : Droby( int a1, int b1); Droby(){a=0;b=0;} void Add(Droby &d); void set_a( int a1){a=a1;} void set_b( int b1){ if (!b1) throw ExceptionZeroDivide(); //выброс созданного исключения b=b1; } int get_a(){ return a;} int get_b(){ return b;} Droby& Sub(Droby &d); }; //реализация объявленных методов Droby::Droby( int a1, int b1) { this ->set_b(b1); a=a1; b=b1; int N=Nod();a= a/N; b= b/N; } int main() { try { Droby D(2,0); } catch ( const ExceptionZeroDivide& err) { std::cout << "Error!!!" << std::endl; } return 0; } Пример создания класса исключений ExceptionZeroDivide как наследника класса std::exception для использования метода what(), для вывода сообщения class ExceptionZeroDivide: public std::exception{ private : std::string message; public : ExceptionZeroDivide(std::string msg): message(msg){} //перелпредекление метода what() класса exception const char * what() const { return message.c_str(); } }; class Droby { private : int a,b; int Nod(); public : Droby( int a1, int b1); Droby(){a=0;b=0;} void Add(Droby &d); void set_a( int a1){a=a1;} void set_b( int b1){ if (!b1) throw // выброс созданного исключения ExceptionZeroDivide( "Zero Divide" ); b=b1; } int get_a(){ return a;} int get_b(){ return b;} Droby& Sub(Droby &d); }; int _tmain( int argc, _TCHAR* argv[]) { try { Droby D(2,0); } catch ( const ExceptionZeroDivide& err) { std::cout << err.what() << std::endl;//вызов переопред. Метода } return 0; } 13 Потоки В основе системы ввода и вывода С++ лежит понятие потока (stream). Поток – это абстрактное понятие, относящееся к любому переносу данных от источника к приемнику.Поток – объект класса, предоставляющего единый логический интерфейс создания или обработки информации в программе. Стандартная система ввода-вывода состоит из сложной системы шаблонных классов. Информация выбирается с устройства и сохраняется на устройстве, поэтому потоки можно классифицировать по виду устройств, с которыми он работает: • стандартные • файловые • строковые Основные операции потока: извлечение данных – чтение данных из устройства; помещение в поток – запись данных в устройство. По направлению обмена потоки можно разделить на: • входные – обеспечивают чтение данных и помещение прочитанных данных в память; • выходные – обеспечивает запись данных в устройство, связанное с потоком; • двунаправленные – как чтение, так и запись данных. Для оптимизации выполнения операций ввода и вывода обмен выполняется с использованием специальной области памяти – буфера. По виду устройств, с которыми работает поток, можно выделить: • стандартные потоки - передача данных от клавиатуры и на экран; • файловые потоки –обмен информацией с файлами; • строковые потоки – работы с массивами символов. Для поддержки потоков библиотека С++ содержит иерархию классов, построенную на основе двух базовых классов – ios и streambuf. Класс ios содержит общие для ввода и вывода поля и методы, класс streambuf обеспечивает буферизацию потоков и их взаимодействие с физическими устройствами. ios базовый класс потоков istream класс входных потоков ostream класс выходных потоков iostream класс двунаправленных потоков istringstream класс входных строковых потоков ostringstream класс выходных строковых потоков stringstream класс двунаправленных потоков ifstream класс входных файловых потоков ofstream класс выходных файловых потоков fstream класс двунаправленных файловых потоков Описание классов находится в заголовочных файлах: |