БлогNot. С++: как обработать исключения самостоятельно?

С++: как обработать исключения самостоятельно?

Конструкция try - throw - catch - часть стандарта С++, как и класс exception, поэтому в простых случаях достаточно поместить "опасный" код в блок try { } и использовать блок catch () { } для вывода сообщений о проблемах, например, здесь мы перехватываем попытку выделить память под 500 миллионов элементов массива:

#include <iostream>
#include <exception>
using namespace std;

int main () {
 int *a=NULL;
  try {
   a = new int[500000000];
  }
  catch (exception &e) {
    cout << "Exception: " << e.what() << endl;
  }
  system ("pause >nul");
  return 0;
}

У меня на машине с небольшим количеством оперативки (2 Гб) в Studio эта программка выдала, как и положено,

Exception: bad allocation

Не сложнее организовать и собственные обработчики исключений, в простейшем случае они могут быть даже просто пустыми классами:

#include <iostream>
#include <cmath>
using namespace std;

//Простейшие обработчики ошибок - классы без компонент:
class ZeroDivide {}; //Деление на ноль
class Overflow {};   //Переполнение

// Определение функции с генерацией исключений:
double dividor(double n, double d) {
 if (fabs(d) < 1e-12) throw ZeroDivide(); // Вызов конструктора класса-обработчика
 double b = n / d;
 if (b > 1e+30) throw Overflow();  // Вызов конструктора класса-обработчика
 return b;
}

int main(void) { 
  double x,y;
  try {
   x = dividor(1,0);
  }
  catch (Overflow) { cerr << "\nOverflow"; x=1e30; }
  catch (ZeroDivide) { cerr << "\nZeroDivide"; x=1e30; }
  cout << "\nResult x = " << x;

  try {
   y = dividor(1e24,1e-9);
  }
  catch (Overflow) { cerr << "\nOverflow"; y=1e30; }
  catch (ZeroDivide) { cerr << "\nZeroDivide"; y=1e30; }
  cout << "\nResult y = " << y;

  system("pause >nul");
  return 0;
}

Как видим, "пересылка" исключений делается через оператор throw, а блок try-catch может содержать реакцию на ошибки и "исправление" значений нужных переменных. Конечно, мы могли не дублировать блок обработки, а вынести его в ещё одну функцию:

#include <iostream>
#include <cmath>
using namespace std;

class ZeroDivide {};
class Overflow {};

double dividor (double n, double d) {
 if (fabs(d) < 1e-12) throw ZeroDivide();
 double b = n / d;
 if (b > 1e+30) throw Overflow();
 return b;
}

double divide (double n, double d) {
 double x;
 try {
  x = dividor (n,d);
 }
 catch (Overflow) { cerr << "\nOverflow"; x=1e30; }
 catch (ZeroDivide) { cerr << "\nZeroDivide"; x=1e30; }
 return x;
}

int main(void) { 
  double x = divide (1,0);
  cout << "\nResult x = " << x;
  double y = divide(1e24,1e-9);
  cout << "\nResult y = " << y;

  system("pause >nul");
  return 0;
}

Если перехватываются все исключения:

try {
 //операторы
}
catch (...) { /* ... */ }

, то такой обрабочик ставится последним в цепочке или единственным. Но вообще-то это опасно делать в современных "навороченных" IDE.

#include <iostream>
#include <exception>
#include <cmath>
using namespace std;
class Error {};

double dividor (double n, double d) {
 if (fabs(d) < 1e-12) throw Error();
 double b = n / d;
 if (b > 1e+30) throw Error();
 return b;
}

double divide (double n, double d) {
 double x;
 try {
  x = dividor (n,d);
 }
 catch (...) { cerr << "\nError"; x=1e30; }
 return x;
}

int main(void) { 
  double x = divide (1,0);
  cout << "\nResult x = " << x;
  double y = divide(1e24,1e-9);
  cout << "\nResult y = " << y;

  system("pause >nul");
  return 0;
}

Наконец, блоки try-catch могут быть вложенными. В этом случае из внутреннего блока можно передавать обработку исключения внешнему оператором throw; без аргументов:

#include <iostream>
using namespace std;

void Compare (int k) { // Функция, генерирующая исключения
 if (k % 2 != 0) throw k; //Нечетное значение (odd)
 throw "Even";            //Четное значение (even)
}

// Функция с контролем и обработкой исключений:
void Func(int j) {
 try {
  try {
   Compare (j); //Вложенный контролируемый блок
  }
  catch (int n) {
   cout << "\nOdd";
   throw; //Ретрансляция исключения 
  }
  catch (const char *Msg) {
   cout << endl << Msg; 
  }
 } // Конец внешнего контролируемого блока
 // Обработка ретранслированного исключения:
 catch (int i) {
  cout << "\nResult = " << i;
 }
}

int main(void) {
 cout << "\nFirst call:";
 Func(4);
 cout << "\nSecond call:";
 Func(7);
 system("pause >nul");
 return 0;
}

В этом примере для чётного значения аргумента k функция Compare формирует исключение типа const char *.

Для нечётного значения создается исключение типа int, равное значению аргумента.

В вызывающей функции Func() - два вложенных контролируемых блока. Во внутреннем из них содержится - два обработчика исключений.

Обработчик catch (int n), приняв исключение, выводит в поток cout сообщение "Odd" и ретранслирует исключение, то есть, передаёт его во внешний контролируемый блок.

Обработка исключения во внешнем блоке стандартна.

В основной программе функция Func() вызывается дважды - с чётным и нечётным аргументами. В первом случае после печати сообщения "Even" функция завершается без выхода из внутреннего контролируемого блока. Во втором случае выполняются две процедуры обработки исключений из двух вложенных контролируемых блоков. Первая из них печатает сообщение "Odd" и ретранслирует исключение. Вторая печатает значение нечётного параметра, снабдив его пояснительным текстом: "Result = 7".

В результате на экране мы увидим:

First call:
Even
Second call:
Odd
Result = 7

Если оператор throw использовать вне контролируемого блока, то вызывается специальная функция terminate(), завершающая выполнение программы.

19.04.2017, 14:09 [4248 просмотров]


теги: c++ программирование ошибка

К этой статье пока нет комментариев, Ваш будет первым