Под исключением понимается некоторая ситуация, возникающая при выполнении программы, вследствие появления которой нормальное продолжение программы невозможно. Дальнейшее выполнение программы определяется программистом на этапе проектирования и носит название обработки исключения. Прерывание программы пользователем или от аппаратуры (например, нажатие клавиш Ctrl-Break, сигнал таймера) как исключения не рассматриваются.
В С++ введен специальный механизм обработки исключений. Он организуется при помощи трех ключевых слов: try, catch и trow.
В блок try помещается или вся программа, или фрагмент ее, в котором может возникнуть ошибка. При возникновении ошибки возбуждается исключение (throw), которое перехватывается и обрабатывается в блоке catch, тип аргумента которого совпадает c типом выражения в операторе throw.
Схема размещения такова:
try { …
throw выражение1;
….
throw выражение2;
…..
}
catch (тип арг1)
{ ….}
catch (тип арг2)
{ ….}
Выражение может быть простой переменной, ссылкой, указателем или вызовом конструктора класса. Аргументами catch должны быть соответственно типы простых переменных, ссылок, указателей или имя класса.
Механизм обработки исключений, вообще говоря, обеспечивает корректное завершение программы: при возникновении исключительной ситуации в блоке try с помощью деструктора уничтожаются созданные в начале блока объекты и сворачиваются соответствующие открытые стеки; при сбоях в работе с файлами последние закрываются и т.д.
Примеры организации исключений.
void main()
{
try {……..
…. throw 1;
……..
…. throw ‘a’;
}//try
catch (int){…….}
catch (char) {…..}
}//main
Операторы throw могут быть включены в функцию, а вызов ее помещен в блок try
void MyFunc(){
……..
if(…)throw 1;
……..}//MyFunc
void main()
{try
{ MyFunc(); ……
}//try
catch(int){……}
}//main
Наконец, блоки try и catch могут быть включены в функцию.
Рекомендуется создавать пользовательские классы исключений.
Например,
class ReadError {
public: ReadError () {cout << “Error Read”;}
};
class WritedError {
public: WriteError () {cout << “Error Write”;}
};
void main ()
{ try{
ifstream in(“имя”);
if(!in) throw ReadError ();
….
ofsteam out (“имя”);
if(!out) throw WriteError();
….
}//try
catch(ReadError){…}
catch(WriteError){….}
}//main
Классы исключений можно объединять в иерархию. Например, класс арифметических ошибок, класс ошибок ввода данных и др. могут быть производными классами некоторого базового класса исключений. (См. ниже контрольные вопросы).
Ниже приводится пример программы с использование контейнера типа vector c объектами класса MyRecord.
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <ctype.h>
#include <algorithm>
using namespace std;
class MyRecord; // Объявление класса
typedef vector <MyRecord> Base; // Переименование типа - необязательно
vector<MyRecord>::iterator index ; //Определение итератора index
class MyRecord{
string Name;
int age;
public:
/* Здесь можно включить поиск ошибок ввода данных,
например, в name не должны вводиться цифры, возраст не может превышать 100 и т.п.*/
void InputRecord()
{
string name;
int len;
cout<<"Input Name & Age"<<endl;
cin>>name;
len=name.length();
for(int i=0; i<len; i++)
if(isdigit(name[i])) throw 1;//isdigit в <сtype.h>
/*Перегрузка операции < для применения алгоритма сортировки из библиотеки
algorithm; сортировка ведется по полю Name*/
bool operator<( MyRecord b)
{
if(Name<b.Name) return true;
return false;
}
};
class MyBase{
Base bs;
MyRecord mr;
public:
//ввод элементов в контейнер
void InputBase()
{
char flag=true;
while (flag)
{
try
{ mr.InputRecord();
bs.push_back(mr);
flag=false;
}//try
catch(int) { cout<<"Once more Name"<<endl; }
}
}
void ShowBase()
{ int n=bs.size();
for (int i=0; i<n; i++)
bs[i].ShowRecord();
}
//Перегрузка индексации для MyBase
MyRecord& operator [](int i)
{return bs[i]; }
//Очистка контейнера
void ClearBase()
{bs.clear(); }
//Вычисление среднего значения элементов age в контейнере
int AverageAge()
{
int n=bs.size();
int buf=0;
for(index=bs.begin(); index!=bs.end(); index++)
buf=buf + (*index).GetAge();
buf=buf/n;
return buf;
}
//Сортировка
void SortBase()
{ sort(bs.begin(),bs.end());}
//Запись в файл содержимого контейнера
void InFile()
{//Здесь включить try на открытие файла
fstream out("c:\\Base.txt",ios::out);
int n=bs.size();
for (int i=0; i<n; i++)
out<<bs[i].GetAge()<<" "<<bs[i].GetName();
}
};
int main()
{
MyBase mb;
mb.InputBase();
mb.ShowBase();
mb.SortBase();
mb.InFile();
mb.ClearBase();
mb.ShowBase();
return 0;
}
Замечания
1.В классе Base инкапсулированы все методы работы с контейнером, благодаря чему main выглядит очень незатейливо.
2. Обратите внимание, что методы ShowBase и AverageAge выполнены по-разному: один с использованием индексации, второй с применением итераторов. В данном случае, т.е. для класса vector, оба метода можно сделать по одному типу – или с индексацией или с итераторами. Но итераторы работают и для других последовательных контейнеров – list и deque, а индексация определена только для vector.
2. В приведенной программе невозможно ввести в контейнер больше одной записи. Исправить это легко – достаточно в main запустить InputBase в цикле. Но делать так не следует, так как этим мы усложним работу клиента. Посмотрите пример из 2-й работы, где массив элементов создается в отдельном классе. Здесь такой класс уже есть и в нем достаточно организовать InputBase такую, что можно было бы добавлять в контейнер несколько элементов без предварительного создания соответствующего массива. Однако, вам придется использовать resize(N) после создания контейнера, вводя значение N по запросу, и комбинировать присваивание и push_back .
Варианты заданий
В нижеследующих вариантах разработать классы с использованием контейнера, разработать методы ввода данных с клавиатуры в контейнер, сохранение элементов в файле, поиск и сортировку элементов в массиве c использованием библиотечных функций и вывод на экран результатов.
Предусмотреть обработку не менее 2-х исключительных ситуаций, возникающих, например, при вводе данных с консоли и работе с файлом; обработчики данных должны указывать на место и причину возникновения ситуации и предлагать способ ее преодоления.
Во всех нижеследующих вариантах написать программу по типу приведенного примера, в основе которой лежат следующие классы:
1. Класс “Студент” с полями: ФИО студента, номер группы, успеваемость(массив из 3-х элементов – количество пятерок, четверок, троек).
Программа должна осуществлять:
−ввод с клавиатуры и из файла в динамический массив не менее 10 записей с проверкой данных;
−сортировку записей по номеру группы;
−вывод в файл и на дисплей фамилии и группы студентов, средний балл которых больше 4.
2. Класс “Рейсы Аэрофлота” с полями: пункт назначения рейса, номер рейса, тип самолета.
−Программа должна осуществлять:
−ввод с клавиатуры с проверкой данных и из файла в динамический массив не менее 7 записей;
−сортировку записей по возрастанию номера рейса;
−вывод в файл и на дисплей номеров рейсов и типов самолетов, вылетающих в пункт назначения, название которого вводится с клавиатуры.
3.Класс “Поезд” с полями: пункт назначения, номер поезда, время отправления.
−Программа должна осуществлять:
−ввод с клавиатуры с проверкой данных и из файла в динамический массив не менее 8 записей;
−сортировку записей по пункту назначения;
−вывод в файл и на дисплей номеров поездов и пунктов назначения, отправляющихся после введенного с клавиатуры времени.
4. Класс “Записная книжка” с полями: ФИО, номер телефона, день рождения (массив из 3-х чисел).
−Программа должна осуществлять:
−ввод с клавиатуры с проверкой данных и из файла в динамический массив не менее 8 записей;
−сортировку записей по дням рождения;
−вывод в файл и на дисплей ФИО и номер телефона человека, дата дня рождения которого вводится с клавиатуры.
5.Класс “Знаки зодиака” с полями: ФИО, знак зодиака, день рождения (массив из 3-х чисел).
−Программа должна осуществлять:
−ввод с клавиатуры и из файла в динамический массив не менее 8 записей с проверкой данных;
−сортировку записей по дате дня рождения;
−вывод в файл и на дисплей дату дня рождения и знак зодиака человека, фамилия которого вводится с клавиатуры.
6.Класс “Цена” с полями: название товара, название магазина, стоимость товара в руб.
−Программа должна осуществлять:
−ввод с клавиатуры и из файла в динамический массив не менее 8 записей с проверкой данных;
−сортировку записей по названию товаров;
−вывод в файл и на дисплей информации о товаре, название которого вводится с клавиатуры.
7.Класс “Счет” с полями: расчетный счет плательщика, расчетный счет покупателя, перечисляемая сумма.
−Программа должна осуществлять:
−ввод с клавиатуры и из файла в динамический массив не менее 8 записей с проверкой данных;
−сортировку записей по расчетным счетам плательщиков;
−вывод в файл и на дисплей информации о сумме, снятой с расчетного счета плательщика, имя которого вводится с клавиатуры.
8.Класс “Университет” с полями: название, город, госуд./коммерческий, количество студентов.
−Программа должна осуществлять:
−ввод с клавиатуры и из файла в динамический массив не менее 8 записей с проверкой данных;
−сортировку записей по названию города;
−вывод в файл и на дисплей информации о товаре, название которого вводится с клавиатуры.
9.Класс “Супермаркет” с полями: название сети (Пятерочка, Метро, Седьмой континент), город, количество наименований товара, специализация(продукты, электроника, и т.д).
−Программа должна осуществлять:
−ввод с клавиатуры и из файла в динамический массив не менее 8 записей с проверкой данных;
−сортировку записей по названию сети;
−вывод в файл и на дисплей информации об имеющихся супермаркетах в городе, название которого вводится с клавиатуры.
10. Класс “Общежитие” с полями: ФИО студента, номер группы, номер комнаты.
−Программа должна осуществлять:
−ввод с клавиатуры и из файла в контейнер не менее 8 записей с проверкой данных;
−сортировку записей по номеру комнаты;
−вывод в файл и на дисплей информации о студентах группы, номер которой вводится с клавиатуры.
11.Класс “Вычислительный центр” с полями: тип материнской платы компьютера, тип процессора, тип монитора.
−Программа должна осуществлять:
−ввод с клавиатуры и из файла в контейнер не менее 8 записей с проверкой данных;
−сортировку записей по типу процессора;
−вывод в файл и на дисплей информации о компьютерах, тип материнской платы которого вводится с клавиатуры.
12.Класс “Работник” с полями: ФИО, должность, год поступления на работу.
−Программа должна осуществлять:
−ввод с клавиатуры и из файла в контейнер не менее 8 записей с проверкой данных;
−сортировку записей алфавиту;
−вывод в файл и на дисплей информации о работниках, чей стаж работы превышает значение, введенное с клавиатуры.
Контрольные вопросы
1. Что представляет из себя контейнер? Покажите контейнер в своей программе.
2. В процессе отладки программы появилась необходимость увеличить массивы, объявленные ниже, на 4 элемента. Можно ли это сделать в первом и во втором случаях, не изменяя константу 10?
int *mas1=new int[10]; // 1
vector <int> mas2(10); //2
3. Если работа с массивами, объявленными выше, заканчивается, то в каком случае обязательно использование оператора delete?
4. Если вы хотя бы приблизительно знаете размер динамического массива в программе, то является ли удачным объявление его как нулевого вектора и почему?
vector <MyClass> my;
5.Можно ли обрабатывать исключительные ситуации, не используя try, throw, catch, и если да, то зачем нужны последние?
6. Какие сообщения и в какой последовательности будут выведены на монитор при а=1 и a=0 и почему?
class Alpha {
public:
Alpha(){cout<<"Constructor A"<<endl;}
~Alpha(){cout<<"Destructor A"<<endl;}
};
void myfunc() {
try {
Alpha alf;
int a;
cin>>a;
if ( a) throw 1;
cout << “End of function”;
}
catch(int) {cout<<"HandleIn"<<endl;}
cout<<"Continue"<<endl;
}//myfunc
int main()
{ myfunc();
return 0;
}
7. Разберитесь в приведенном фрагменте программы и ответьте, почему в иерархии исключений единственный обработчик запускает нужную функцию ErrProcess.
//Базовый класс обработки ошибок
class MathError {
/*…*/
virtual void ErrProcess() {};
}; // MathError
//Класс ошибок переполнения
class Overflow:public MathError {
/*…*/
void ErrProcess (){/*…*/};
}; // Overflow
//класс ошибок деления на 0
class ZeroDivide: public MathError {
/*…*/
void ErrProcess() {/*…*/};
}; // ZeroDivide
/* …………*/
try {
/* В точках возникновения ошибок записываются операторы
if(…) throw Overflow vr;
или
if(…) throw ZeroDivide vr;…
*/
}
// единственный обработчик для всех типов исключений