C++. Бархатный путь. Часть 2 - Интерактивный вычислитель частного от деления двух плавающих чисел
ОГЛАВЛЕНИЕ
На очереди ещё одна простая программа. Это уже не полигон. Это интерактивный вычислитель частного от деления двух плавающих чисел. Эта программа в бесконечном цикле запрашивает значения делимого и делителя, а в случае возникновения исключительной ситуации возбуждает исключение. В момент возбуждения исключения, пользователю предоставляется возможность принятия решения относительно дальнейшего продолжения вычислений.
Представляющий исключение класс MyDivideByZeroError обладает всем необходимым набором элементов для эффективного взаимодействия с системой управления исключениями. Он располагает конструктором умолчания для возбуждения исключения. Там же имеется конструктор копирования для инициализации соответствующего параметра в блоке перехвата исключения. Есть и деструктор, который обеспечивает освобождение динамической памяти.
#include <iostream.h>
#include <string.h>
#define YESMESS "Мы продолжаем."
#define NOMESS "Мы завершаем."
class MyDivideByZeroError
{
char *MyErrorMessage;
public:
char ContinueKey;
MyDivideByZeroError(): MyErrorMessage(NULL)
{
char YesKey;
cout << "Зафиксировано деление на нуль." << endl;
cout << "Принимать экстренные меры? (Y/N) >> ";
cin >> YesKey;
if ( YesKey == 'Y' || YesKey == 'y' )
{
ContinueKey = 1;
MyErrorMessage = strdup(YESMESS);
}
else
{
ContinueKey = 0;
MyErrorMessage = strdup(NOMESS);
}
}
MyDivideByZeroError(const MyDivideByZeroError& CopyVal)
{
ContinueKey = CopyVal.ContinueKey;
MyErrorMessage = strdup(CopyVal.MyErrorMessage);
}
~MyDivideByZeroError()
{
if (MyErrorMessage) delete(MyErrorMessage);
}
void PrintMessage()
{
cout << MyErrorMessage << endl;
}
};
float Dividor(float, float) throw(MyDivideByZeroError);
void main()
{
float MyVal1, MyVal2;
for (;;)
{
//__ Начало контролируемого блока __________________________________.
try
{
cout << "========================================" << endl;
cout << "MyVal1 >> ";
cin >> MyVal1;
cout << "MyVal2 >> ";
cin >> MyVal2;
cout << "Считаем... " << Dividor(MyVal1, MyVal2) << endl;
cout << "Получилось! ";
}
catch (MyDivideByZeroError MyExcept)
{
MyExcept.PrintMessage();
if (MyExcept.ContinueKey == 0)
{
cout << "Надоело воевать с ошибками! Уходим." << endl;
break;
}
}
//__ За пределами контролируемого блока ____________________________.
cout << "Уже за пределами блока. Мы продолжаем..." << endl;
}
}
float Dividor(float Val1, float Val2) throw(MyDivideByZeroError)
{
if (Val2 == 0.0) throw MyDivideByZeroError();
return Val1/Val2;
}
И, наконец, пример замещения функций unexpected и terminate. Последняя программа в этой книге.
#include <iostream.h>
#include <except.h>
#define MAXERR 5
class MaxError;
class MyError
{
public:
MyError()
{
CounterError++;
if (CounterError > MAXERR)
{
cout << " Здесь MyError()... throw MaxError()!" << endl;
throw MaxError();
}
else
{
cout << " Здесь MyError()... CounterError++!" << endl;
}
}
void ErrSay()
{
cout << " Здесь ErrSay(): " << CounterError << endl;
}
static int CounterError;
};
int MyError::CounterError = 0;
class MaxError
{
public:
MaxError()
{
if (CounterMaxError == 0)
{
/*
MaxError один раз может подправить значение счётчика MyError::CounterError.
*/
CounterMaxError++;
MyError::CounterError -= 2;
cout << "Здесь MaxError().. MyError::CounterError-= 2;" << endl;
}
else
{
cout << " Здесь MaxError()... ###" << endl;
}
}
static int CounterMaxError;
};
int MaxError::CounterMaxError = 0;
void RunnerProcessor();
void Run() throw(MyError);
void MyUnex();
void MyTerm();
void main()
{
unexpected_function OldUnex;
terminate_function OldTerm;
OldUnex = set_unexpected(MyUnex);
OldTerm = set_terminate(MyTerm);
/*
Мы замещаем функции unexpected() и terminate().
Адресные переменные нужны для того, чтобы запомнить адреса старых
функций. В случае необходимости, их можно восстановить:
set_unexpected(OldUnex);
set_terminate(OldTerm);
*/
RunnerProcessor();
}
void RunnerProcessor()
{
for (;;)
{
try
{
Run();
}
catch (MyError err)
{
err.ErrSay();
}
}
}
void Run() throw(MyError)
{
cout << "Работает Run()..." << endl;
throw MyError();
}
void MyUnex()
{
/* Мы всё ещё находимся в пределах try-блока. */
cout << "Это MyUnex()..." << endl;
throw MyError();
}
void MyTerm()
{
int MyTermKey = 0;
/*
Вышли из try-блока. Включилась система автоматического торможения.
*/
for ( ; MyTermKey < 5; )
{
cout << "Это MyTerm()........................" << MyTermKey << endl;
MyError::CounterError = 0;
MaxError::CounterMaxError = 0;
RunnerProcessor();
MyTermKey += 1; /* Цикл здесь уже не циклится! */
}
MaxError::CounterMaxError = 0;
throw MyError(); /* Исключения не работают! */
}
Всё. Приехали. Можно расслабиться. Можно постоять на берегу океана. Послушать шум ветра в соснах. Посмотреть на касаток в холодной прозрачной воде. Только недолго. Впереди ждут великие дела.