- •Раздел 4. Разработка по Тема 4.1. Проектирование интерфейса с пользователем
- •4.1.1. Типы пользовательских интерфейсов.
- •4.1.2. Пользовательская и программная модели интерфейса.
- •4.1.3. Разработка диалогов.
- •4.1.4. Основные компоненты графических пользовательских интерфейсов.
- •Тема 4.2. Реализация графических пользовательских интерфейсов.
- •4.2.1. Диалоги, управляемые пользователем.
- •4.2.2. Диалоги, управляемые системой.
- •4.2.3. Использование метафор.
- •4.2.4. Технология Drag and Drop.
- •4.2.5. Интеллектуальные элементы.
- •4.3.1. Базовые типы данных.
- •Константы
- •Область действия имен
- •4.3.2. Указатели и адресная арифметика.
- •4.3.3. Составные типы данных. Структуры
- •Битовые поля
- •Определение типов
- •Перечислимые типы
- •4.3.4. Выражения и операции.
- •4.3.5. Управляющие конструкции. Условные операторы
- •Операторы циклов
- •4.4.1. Статические одномерные массивы.
- •4.4.2. Статические многомерные массивы.
- •4.4.3. Динамические массивы.
- •4.4.4. Массивы указателей.
- •4.5.1. Стеки.
- •4.5.2. Очереди.
- •4.5.3. Списки.
- •4.5.4. Бинарные деревья.
- •4.6.1. Объявление классов и экземпляров классов.
- •4.6.2. Инкапсуляция данных и методов.
- •4.6.3. Конструкторы классов.
- •Конструктор по умолчанию
- •Конструктор копирования
- •4.6.4. Деструкторы классов.
- •4.7.1. Разделы в описании класса.
- •4.7.2. Friend-конструкции.
- •4.7.3. Статические члены классов.
- •4.7.4. Использование описателя const в классах.
- •4.8.1. Вложенность классов.
- •4.8.2. Наследование данных и методов.
- •4.8.3. Типы наследования.
- •4.9.1. Полиморфизм раннего связывания.
- •4.9.2. Полиморфизм позднего связывания и виртуальные функции.
- •4.9.3. Абстрактные методы и классы.
- •4.10.1. Функции консольного ввода-вывода.
- •4.10.2. Функции файлового ввода-вывода.
- •4.10.3. Использование библиотеки классов потокового ввода-вывода.
- •4.11.1. Перегрузка операций.
- •4.11.2. Шаблоны функций.
- •4.11.3. Шаблоны классов.
- •4.11.4. Обработка исключений.
- •Тема 4.12. Com-технология.
- •4.12.1. Основные понятия.
- •4.12.2. Типы интерфейсов.
- •Свойства интерфейсов
- •Типы интерфейсов
- •4.12.3. Типы com-объектов.
- •4.12.4. Фабрика классов.
- •Тема 4.13. Построение com-сервера.
- •4.13.1. Язык idl.
- •Содержимое файла idl
- •4.13.2. Определение пользовательского интерфейса.
- •4.13.3. Реализация пользовательского интерфейса.
- •4.13.4. Создание тестового клиента.
- •Тема 4.14. Обзор платформы ms .Net.
- •4.14.1. Общая идея архитектуры .Net.
- •4.14.2. Достоинства и недостатки .Net.
- •4.14.3. Схема трансляции программ в .Net.
- •4.14.4. Язык msil.
- •4.14.5. Объектно-ориентированная модель .Net.
4.11.4. Обработка исключений.
Имеется ввиду обработка анормальных (ошибочных) ситуаций, возникающих на этапе исполнения программы. Сам процесс обработки обозначается термином Exception Handling. Выявление и обработка аварийных ситуаций производится с помощью специальных конструкций, использующих ключевые слова try, catch и throw.
Если не разрабатывать своих собственных обработчиков ситуаций (handlers), то компилятор по умолчанию создает стандартную процедуру завершения программы (termination). Однако обозначение конкретной аварийной ситуации (throwing an exception) позволяет предпринять какие-то определенные действия по ее обработке и, возможно, продолжению работы программы. При этом рассматриваются только ситуации внутреннего (synchronous) характера (нет памяти в области heap, не найден файл, переполнение и т. д.). Ситуация, созданная нажатием клавиш Ctrl+C, считается внешней и не может быть обработана.
Блок кодов вида try {...} выделяет ту часть программы, в которой ожидаются аварийные ситуации, и на них предусмотрен и определен перечень нестандартных действий. Непосредственно за ним должен следовать хотя бы один блок вида catch () {...}, который и является обработчиком ситуации. Например:
void main()
{
long double **array;
long n=l;
cout « "\n\nAllocation Exception Demonstration\n\n";
while (n)
{
cout « "Enter matrix dimension (0 - to exit): "; cin » n;
try // Блок, в котором возможна авария
{
array = new long double* [n];
if (! array) // Так мы узнаем об отказе
throw "Allocation failure"; // Выбрасываем флаг аварии
for (int 1=0; i<n: i++)
array[i] = new long double[n];
If (!array[i]) throw "Allocation failureXn";
}
catch (char* s)
{ // Обработчик ситуации
cout « s « n «" elements\n\n";
}
}
При тестировании этого примера имейте в виду, что система пытается найти место в пространстве виртуальной памяти процесса и на это требуется некоторое время. Поэтому реакция не будет мгновенной. Реакция системы осуществляется, только если ситуация возникает в блоке кодов после ключевого слова try. При возникновении аварии система ищет нужный обработчик (их может быть несколько), затем освобождает стек и передает управление обработчику ситуации, то есть блоку кодов найденного оператора catch. Если обработчик не найден, то управление передается действующей в данный момент функции terminate, которую можно переопределять.
В рассмотренном примере исключительная ситуация, если она наступает, создается операцией new. Причем мы сами выбрасываем флаг аварии. Для этой цели используется оператор throw, который имеет несколько возможных форм:
• throw <expression> — эта форма в случае аварии передает обработчику выражение, определяемое программистом;
• throw; — эта форма повторно вызывает уже выбранный в качестве текущего обработчик;
• void func() throw() { тело функции } — в этой форме указано, что функция void func() не должна создавать аварийных ситуаций, или что они обрабатываются внутри func. Если все же они возникнут, то управление передается действующей в данный момент функции unexpected(), которая по умолчанию вызывает функцию abort();
• void func() throw(A, В) { тело функции } — эта форма throw определяет список аварийных ситуаций, которые может выбросить функция func().
Если возникнет другая, не указанная ситуация, то, как и в предыдущем случае, будет вызвана функция unexpected(). Стандартная обработка неожиданных исключений может быть изменена путем переопределения функции unexpected(). Например, оператор
set_unexpected (MyHandler);
задает функцию void MyHandler() обработчиком неожиданных исключений, начиная с точки задания. Функция MyHandler() должна быть заранее определена и находиться в области видимости. Обработчиком ситуации является один из catch-блоков. Они должны следовать за try-блоком. Синтаксически возможны две формы catch-блока:
catch (Type х) {}
catch (...){}
Первая форма позволяет обработать объект х типа Туре, который выбрасывает блок try в точке throw. Вторая позволяет обработать любую исключительную ситуацию независимо от типа. Если она используется, то должна стоять последней в списке обработчиков. Аварийная ситуация считается пойманной, если ее тип совпадает с типом, указанным в операторе catch(). После обработки ситуации управление передается в точку программы, которая следует за последним блоком catch.
Int x;
…
try
{
if (x<0) throw 0;
else if (x> MAX_VALUE) throw “Out of scope”;
}
catch (int)
{…}
catch (char *error_message)
{ puts (error_message);
trow; // передаем обработку во внешний блок с контролем,
// который в примере не показан
}
catch(…) // использоваться не будет, т.к. все ситуации предусмотрены
{
…
}
Суффикс throw() или throw(Error) при какой-либо функции не влияет на тип функции. Так, две функции int f() throw(){} и int f(){} расцениваются компилятором как повторное определение одной и той же функции. Для сравнения вспомните, что суффикс const изменяет тип функции. В связи с этим надо проявлять осторожность при описании виртуальных функций. Переопределение виртуальной функции в производном классе должно иметь такой же суффикс, как и в базовом. Если суффикс содержит имя класса, например throw (А), то объект класса, производного от class А, считается удовлетворяющим заданной ситуации. Например:
class A
{ // Базовый класс
protected:
int j;
publiс:
A (int k=0) { j=k; }
virtual int getj() throw(A);
}
int A :: getj ()throw(A)
{
if (j== 0) throw *this; // Аварийная ситуация
return j;
}
class В: public A :// Произв
{ public
В (int k=0) ;
A (k) {}
int getj() throw(B);
}
int В :: getj() throw(B)
{
i
//Аварийная
ситуация
throw *this;
return j;
}
void main()
{
A a(l), aa;
В b(2), Bb;
cout«"\n Exception Handling Demonstration\n\n";
try
{
cout « "\nj=" « a.getj (); cout « "\nj=" « aa.getj();
}
catch (A x)
{
cout«"\n Exception j=0. Class A";
}
try
{
cout « "\nj=" « b.getj (); cout « "\nj=" «bb.getj();
}
catch (A x)
{
cout«"\n Exception j=0. Class B";
}
cout « "\n\n Execution continues after exception " <<
" handling in the catch() block"«"\n\n";
}
В данном примере демонстрируется «вылавливание» ситуации: равенство нулю поля j в объектах класса А или равенство нулю того же, наследованного поля в объектах производного класса В. Обработчик ситуации catch (A x) {} в обоих случаях справляется с задачей поймать объект одного из классов иерархии. Ситуацию обозначаем мы сами в виртуальной функции getj.
Мы использовали два блока try-catch с целью иллюстрации идентичности объектов (аа,№) в смысле реакции системы на ситуацию j==0. Если все коды разместить в одном блоке try { } и оставить один блок catch(), то программа прервется после первой же ситуации j==0. Отметим, что оператор catch (A x) в нашем случае можно заменить на catch (...), и результат будет тем же.