Борис Пахомов Санкт Петербург бхв петербург 2013 удк 004. 4 Ббк 32. 973. 26018. 2 П
![]()
|
try, и С помощью этих операторов и обрабатываются исключительные ситуации. Синтаксис объявления операторов таков try { /* здесь находится участок программы, в котором могут возникнуть исключительные ситуации */ } 460 Часть II. Приложения Windows F orm [ catch (объявление исключения) { /* операторы для обработки возникшего в блоке исключения */ } [catch (объявление исключения) { // операторы, обрабатывающие другой тип исключения } ] ... ] throw выражение Оператор throw выбрасывает для последующей обработки возникающее исключение. Тело оператора catch — это фактически и есть обработчик исключения. Исключение выбрасывается оператором throw , захватывается оператором catch ив его теле обрабатывается. Объявление исключения в операторе catch определяет тип исключения, которое станет обрабатываться в catch . Этим типом может служить конкретный тип данного, определенный в языке, в том числе и класс. Если в объявлении исключения задано многоточие ( ), то это означает, что оператор catch станет обрабатывать любой тип возникающего исключения. Такой catch должен быть последним в блоке. По синтаксису вы заметили, что блоков catch может быть более одного если вы хотите отлавливать конкретные исключения, то сформируйте для каждого из них свой catch , в котором задайте тип обрабатываемого исключения. Необязательный операнд (выражение) в throw имеет смысл операнда в операторе return : тоже возвращает нечто. Более детально процесс обработки исключительной ситуации состоит в следующем Система исполняет операторы программы в обычном режиме, не обращая внимания на наличие операторов обработки исключительных ситуаций. Как только в момент исполнения участка программы в теле try возникает исключительная ситуация, из выражения, находящегося в операторе throw (те. выражение в этом операторе должно всегда присутствовать, создается объект класса Exceptions (просто вызывается конструктор этого класса) и тут же отыскивается наивысший по иерархии оператор catch , который может обработать исключение возникшего типа (или любого типа. Если подходящего по возникшему типу исключения оператора catch не находится, то отыскивается ближайший следующий блок (как вы уже, наверно, догадались, блоков может быть много, потому что исключительные ситуации могут возникать в различных участках вашей программы и каждый из таких подозрительных участков надо охватить блоком. Этот процесс продолжается, пока самый крайний блок не проверится. Глава 13. Управление исключительными ситуациями Если все же подходящий блок не будет обнаружен или в момент такого поиска возникнет новое исключение, вызывается специальная функция среды, которая завершает выполнение приложения. Если же подходящий блок найдется, то начнется обработка исключения соответствующим оператором Пример В этом примере обнаруживается ошибка превышения длины вводимой с клавиатуры строки для ее размещения выделено всего 10 байтов, а мы вводим более 10 байтов. Хотя функция getline() контролирует количество вводимых символов с помощью своего го параметра lim , но он задан большим, чем 10, и поэтому данный контроль не срабатывает. Создадим консольную программу, текст которой приведен в листинге 13.1, результат работы программы показан на рис. 13.1. Листинг 13.1 // 13.1_2011_ Исключения : main project file. #include "stdafx.h" using namespace System; #include "stdafx.h" #include #include #include #define eof -1 //Ctrl+z #define maxline 1000 /* Функция getline(s,lim) вводит с клавиатуры строку в s и возвращает длину введенной строки с учетом cимвола '\0'; lim — максимальное количество символов, которое можно ввести в строку s*/ int getline(char s[],int lim) throw(...) /* Внутри функции могут возникать любые исключения еще один вариант задания выброски исключения. */ { int c,i; for(i=0; i 462 Часть II. Приложения Windows F orm return(i); } //------------------------------------------- int main() { char *buf; try { buf = new char[10]; отвели место для строки из 10 символов getline(buf,200); Ошиблись макс. длина строки задана в 200 символов throw 1; Этот оператор выбросит исключение, если оно возникнет } catch(...) обработчик исключения любого типа { Исключение Console::ReadLine(); Задержка экрана } } Рис 13.1. Обработка любой исключительной ситуации Пример Этот пример развивает обработку исключительных ситуаций. Дополним предыдущую программу обработкой введенных символов. В частности, зададим оператор перевода го символа введенной строки в число. Если этот символ будет буквой, должна возникнуть исключительная ситуация. Чтобы отловить это исключение, надо новый оператор поместить в свой блок try , для которого определить свой блок catch (получаем вложенность блоков обработки исключений. Текст программы показан в листинге 13.2, а результат — на рис. 13.2. Листинг 13.2 // 13.2_2011_ Исключения : main project file. #include "stdafx.h" #include #include Глава 13. Управление исключительными ситуациями #include #define eof -1 //Ctrl+z #define maxline 1000 /* Функция getline(s,lim) вводит с клавиатуры строку в s и возвращает длину введенной строки с учетом cимвола '\0'; lim — максимальное количество символов, которое можно ввести в строку s*/ int getline(char s[],int lim) { int c,i; for(i=0; i //------------------------------------------- int main() { char *buf; try { buf = new char[10]; отвели место для строки из 10 символов getline(buf,200); Ошиблись макс. длина строки задана в 200 символов throw 1; Этот оператор выбросит исключение, если оно возникнет try { int i=buf[0]; } catch(int) { printf("Convertion Exception!\n"); } } catch(...) обработчик исключения любого типа (должен быть последним) { printf("Exception!\n"); задержка экрана } } Заметим, что первым сработал самый внутренний обработчик. Если бы преобразование в число было правильным, то сработал бы уже внешний обработчик (если бы было превышение количества введенных символов. 464 Часть II. Приложения Windows F orm Рис 13.2. Обработка преобразования данных Классы типов исключений При обработке исключений можно задавать классы типов исключений. Например, обрабатывать все исключительные ситуации из класса исключений, возникающих при выполнении арифметических операций и т. п. Все типы таких классов расположены в пространстве System . В табл. 13.1 представлены некоторые классы. Таблица Классы типов исключений Класс Исключительная ситуация возникает Когда идет попытка чтения/записи в защищенной памяти Когда идет попытка доступа к незагружаемой области приложения ApplicationException Когда в приложении возникает нефатальная ошибка ArgumentException Когда один из аргументов метода недействителен ArgumentNullException Когда методу передается нулевая ссылка, а метод ее не распознает как действительный аргумент Когда значение аргумента выходит заграницы, определенные методом При ошибках в арифметических операциях и при преобразовании данных ArrayTypeMismatchException При попытке поместить элемент в массив, тип которого не совпадает с типом элемента DivideByZeroException Когда идет попытка деления числа на ноль EntryPointNotFoundException При попытке загрузить класс в случае отсутствия точки входа (например, в приложении отсутствует метод main() ) Exception В момент выполнения приложения При попытке доступа внутри класса к членам с атрибутами private или protected FormatException Когда обнаруживается нарушение формата аргумента, объявленного в методе Глава 13. Управление исключительными ситуациями Таблица 13.1 (окончание) Класс Исключительная ситуация возникает При выходе индекса массива заграницы массива InsufficientMemoryException Из- за нехватки памяти InvalidCastException При неправильном преобразовании данных InvalidOperationException Когда для текущего состояния объекта вызов метода неверен MemberAccessException При неудачной попытке доступа к члену класса MethodAccessException При неудачной попытке доступа кили- методу внутри класса MissingFieldException При попытке динамического доступа к несуществующему полю класса MissingMemberException При попытке динамического доступа к несуществующему члену класса При попытке динамического доступа к несуществующему методу класса При обработке несуществующего или бесконечного числа с плавающей точкой NotSupportedException При вызове метода, который не поддерживается, или при работе с потоком данных, когда требуемая функциональность не поддерживается NullReferenceException При попытке разыменовать нулевую ссылку OutOfMemoryException При нехватке памяти для продолжения работы программы OverflowException При превышении значений арифметических операций или операций преобразования в контролируемом контексте PlatformNotSupportedException При попытке запустить приложение на неподдерживае- мой платформе При попытке передачи массива с неверной размерностью в метод StackOverflowException При переполнении стека (слишком много вызовов методов По истечении времени, отведенному для процесса или операции TypeLoadException При сбоев момент загрузки некоторого типа TypeUnloadedException При попытке доступа к незагруженному классу UnauthorizedAccessException При попытке неавторизованного доступа (операционная система отвергает доступ из- за ошибки ввода/вывода или из- за нарушения специального типа безопасности При обнаружении неверного Uniform Resource Identifier (URI) 466 Часть II. Приложения Windows F orm Пример В этом примере мы покажем, как использовать класс, задающий определенный тип исключения. Для этого воспользуемся классом из только что приведенной таблицы классом обработки исключительных ситуаций, возникающих при выполнении арифметических действий и преобразований — классом Зададим такой алгоритм возьмем массив целых чисел, датчик случайных чисел в интервале [1, 10] и организуем деление каждого элемента массива на полученное случайное число следующим образом й элемент массива делим на е случайное число, й — на е и т. д. Для простоты мы взяли массив из трех элементов. Поместим в форму две кнопки, одна из которых будет запускать на выполнение наш алгоритма другая обеспечит выход из приложения. Текст обработчиков событий этих кнопок приведен в листинге 13.3, а результат расчетов — на рис. 13.3. Листинг 13.3 private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) { array счетчика while в виде строки /* разделим й элемент массива m[] на е случайное число, й — на е и т. д int j1; int i; int k=0; try { while(k < 1000) { for(i=0; i < m->Length; i++) { Random ^r = gcnew Random(); Формирование обращения к датчику случайных чисел int выдает случайное число в интервале [0,10] switch(i) { case 0: j1= (int) m[i] / j; break; case 1: j1= (int) m[i] / j; break; case 2: j1= (int) m[i] / j; break; default: break; } //switch } //for Глава 13. Управление исключительными ситуациями k++; } //while return; см. пояснение после листинга throw 1; } //try catch (ArithmeticException ^e) // Задаем тип исключений, которые будут обрабатываться { k1[0]=k; для легкого преобразования int в String ^ Системное сообщение" + e->Message + " Шаг цикла" + Convert::ToString(k1[0]); } //catch } //Button1_click private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) { this->Close(); } Рис 13.3. Исключение при делении на ноль Кнопку Пуск надо нажимать, пока не поймается случайная ситуация деления на ноль. Основные пояснения сделаны в тексте программы. А сейчас обратим внимание на два момента как формировать блок, где находится оператор throw , который выдает исключение как избежать неприятностей преобразования числовых типов данных в строку при работе в режиме CLR, в котором мы создали приложение. Пояснение 468 Часть II. Приложения Windows F orm Что касается первого вопроса, то тут надо иметь ввиду следующее оператор throw кое-куда ставить нельзя, ибо он выбрасывает системе исключение не только когда наступает действительная причина его возникновения, но и тогда, когда он сам выполнится. В обычном режиме работы программы этот оператор не должен попадаться для исполнения походу выполнения вашей программы, иначе он сам прервет выполнение программы системным образом (на catch ваша программа в этом случае не попадет и сработает системный вариант. Поэтому блок надо формировать так, чтобы содержащийся в нем throw не выполнялся (тогда он сработает в момент появления в вашей программе исключительной ситуации, и управление будет передано на соответствующий блоку оператор catch ). Что касается второго вопроса, то здесь правило такое среда CLR — среда специфическая и работает со своими типами данных (с объектами, а нес теми, к которым мы привыкли ( int , float , char и т. п. Одними из объектов являются managed- массивы (типы) и их элементы. Объекты имеют метод ToString() , переводящий объект в строку символов. Числовому объекту легко присвоить просто обычное число, а объект уже можно перевести в строку и потом вывести эту строку. Функции, выдающие исключения В соответствии с моделью создания и обработки исключительных ситуаций, принятой в данной версии С, компилятор предполагает, что исключения могут выдаваться только от выполнения оператора throw или от вызова функции. Функция будет выдавать исключения, если ее заголовок дополнен одной из следующих спецификаций функция выдает пустое исключение (те. не выдает никакого исключения функция выдает любое исключение, которое возникнет при ее вызове функция выдает исключение заданного типа. В листинге 13.4 приводится функция f1() , выдающая исключение, когда ее аргумент четное число. Результат работы представлен на рис. 13.4. Листинг 13.4 // 13.4_2011 Исключения : main project file. #include "stdafx.h" #include #include #include { Глава 13. Управление исключительными ситуациями if(i%2 == четное число (остаток отделения равен 0) throw 1; } void f2(int j) throw(...) { f1(j); } //------------------------------ int main() { for(int j=0; j < 10; j++) { try { f2(j); } catch(...) { printf("j=%d\n",j); _getch(); задержка экрана } } //for } Рис 13.4. Числа, в интервале [0.10), на которых возникают исключения 470 Часть II. Приложения Windows F orm ГЛАВА Преобразование между нерегулируемыми и регулируемыми (режим CLR) указателями На практике часто встречаются случаи, когда требуется переходить от обычных указателей к регулируемыми наоборот (еще говорят "переход от среды к среде и наоборот. Этот процесс называют маршалингом. Среда VC++ содержит специальную библиотеку, предназначенную для этих целей. Эту библиотеку можно использовать без так называемого marshal_context Class , однако некоторые преобразования требуют наличия этого класса. Другие преобразования используют функцию marshal_as() . В табл. 14.1 представлены поддерживаемые преобразования с учетом требования контекста, задаваемого классом marshal_context Таблица Преобразование между нерегулируемыми и регулируемыми указателями Из типа В тип Marshal- метод Какой файл надо подключать (Include file) System::String^ const char * marshal_context marshal.h const char * System::String^ marshal_as marshal.h char * System::String^ marshal_as marshal.h System::String^ const wchar_t* marshal_context marshal.h const wchar_t * System::String^ marshal_as marshal.h wchar_t * System::String^ marshal_as marshal.h System::IntPtr HANDLE marshal_as marshal_windows.h HANDLE System::IntPtr marshal_as marshal_windows.h System::String^ BSTR marshal_context marshal_windows.h BSTR System::String^ marshal_as marshal.h System::String^ bstr_t marshal_as marshal_windows.h bstr_t System::String^ marshal_as marshal_windows.h Спуск 472 Часть II. Приложения Windows Таблица 14.1 (окончание) Из типа В тип Marshal- метод Какой файл надо подключать (Include file) System::String^ std::string marshal_as marshal_cppstd.h std::string System::String^ marshal_as marshal_cppstd.h System::String^ std::wstring marshal_as marshal_cppstd.h std::wstring System::String^ marshal_as marshal_cppstd.h System::String^ CStringT CStringT System::String^ marshal_as marshal_atl.h System::String^ CStringT CStringT System::String^ marshal_as marshal_atl.h System::String^ CComBSTR marshal_as marshal_atl.h CComBSTR System::String^ marshal_as marshal_atl.h Маршалинг требует контекстного файла только в том случае, когда выполняется преобразование из управляемого типа в родной (native), нерегулируемый тип, и при этом тип, в который идет преобразование, не имеет деструктора для автоматического освобождения памяти от объекта. Вот тогда маршалинг-контекст разрушает размещенный объект типа своим деструктором. Поэтому преобразования, требующие маршалинг-контекста, действительны только до момента удаления контекста. Чтобы сохранить любое маршалинг-значение, вы должны скопировать его в свою собственную переменную. Если строке присвоено значение NULL , результат преобразования такой строки не гарантируется. На практике для использования маршалинга достаточно подключить к вашей программе пространство имен using namespace Пример 1. Перевод строки |