Исключения в С++ (exception)

Всем нам интуитивно понятно что такое исключение. Это некое действие, которое вступает в силу, при наступлении нестандартной ситуации.  Говоря о об исключениях в С++, рассмотрим одну из таких нестандартных ситуаций — деление на 0. Представьте, что мы работаем в программе, где вручную вносим много числовых данных. Далее, в результате каких-то расчетов программы, значение одной из переменных станет равным 0. А в следующей строке кода это значение выступает делителем. Конечно, это действие приведет к неизбежному закрытию программы и выходу из нее. И вы можете себе представить свое состояние, когда полчаса вводите данные, работая в этой программе, и на каком-то этапе она просто перестает работать и закрывается, а все данные пропадают.

В этой статье будет приведен очень простой  и короткий пример, который поможет разобраться с исключениями в С++. В нем пользователю будет предложено ввести два числа и программа проведет простые расчеты с этими числами. Ввод и расчеты будут проводиться определенное количество раз  — с помощью цикла while.  

Сначала наберите исходный код ниже и скомпилируйте. В переменную num2 внесите число 0, чтобы у вас было понимание той проблемы, что если в программе встречается деление на 0, она резко прекращает работу, хотите вы этого или нет.  

#include <iostream>;
using namespace std;

int main()
{
	setlocale(LC_ALL, "rus");

	int num1;
	int num2;
	int var = 2; //управляющая переменная для while

	while(var--) //пока var - истина (не равно 0)
	{
		cout << "Введите значение num1: ";
		cin >> num1;
		cout << "Введите значение num2: ";
		cin >> num2;

		cout << "num1 + num2 = " << num1 + num2 << endl;
		cout << "num1 / num2 = " << num1 / num2 << endl;
		cout << "num1 - num2 = " << num1 - num2 << endl;
		cout << "=================================" << endl << endl;
	}

	cout << "Программа завершила работу!" << endl << endl;

return 0;
}

В программе цикл должен отработать два раза. В строке 12 мы передаем циклу значение переменной var и в этой же строке уменьшаем её на единицу, используя операцию декремента (var--). Но, если в переменную num2 будет введено значение 0,  мы увидим только результат сложения  a + b  и программа завершит работу не зависимо от того, есть код ниже или нет, отработал цикл или нет.

Используя исключения, мы сможем избежать таких проблем. Чтобы «прикрутить» исключение к этому примеру, надо познакомиться со следующими командами С++: throw  (в переводе — обработать, запустить), try (попытка), catch (поймать, ловить).В исходном коде ниже, исключение сработает так: программа получает конкретное указание от программиста — если значение определённой переменной в определённом участке кода (в try-блоке) будет равно 0, то в этом случае пусть генерируется исключение throw. Это исключение автоматически передастся catch-блоку в виде параметра и выполнится код этого блока. 

Сразу рассмотрим пример. А все подробные пояснения по исходному коду и теория будут располагаться  под ним:

#include <iostream>;
using namespace std;

int main()
{
	setlocale(LC_ALL, "rus");

	int num1;
	int num2;
	int var = 2; 

	while(var--) 
	{
		cout << "Введите значение num1: ";
		cin >> num1;
		cout << "Введите значение num2: ";
		cin >> num2;

		cout << "num1 + num2 = " << num1 + num2 << endl;
		cout << "num1 / num2 = ";

		try //код, который может привести к ошибке, располагается тут
		{	
			if (num2 == 0)
			{
				throw 123; //генерировать целое число 123
			}
			cout << num1 / num2 << endl;	
		}
		catch(int i)//сюда передастся число 123
		{
			cout << "Ошибка №" << i << " - на 0 делить нельзя!!!!" << endl;
		}

		cout << "num1 - num2 = " << num1 - num2 << endl;
		cout << "=================================" << endl << endl;
	}

	cout << "Программа завершила работу!" << endl << endl;;

return 0;
}

В строках 22 — 29 определен try-блок. В нем располагается код, который потенциально может вызвать ошибку в работе программы, а именно ошибку в случае деления на 0. Задаем условие if — если num2 равно 0, то генерировать  целое число 123,к примеру. В этом случае try-блок сразу прекращает выполнение дальнейших команд, а число 123 «падает» в catch. В нашем примере он выводит сообщение об ошибке. При этом программа продолжает работать и  выполнять команды, размещенные ниже. Если же число num2 не будет равно нулю, то в try-блоке  выполнится команда cout << num1 / num2 << endl; ,а catch не сработает.

Запускаем программу и в первом шаге цикла вводим значение переменной num2 равное 0, а во втором — любое другое число:

CppStudio.com

Введите значение num1: 50
Введите значение num2: 0
num1 + num2 = 50
num1 / num2 = Ошибка №123 — на 0 делить нельзя!!!!
num1 — num2 = 50
=================================

Введите значение num1: 50
Введите значение num2: 5
num1 + num2 = 55
num1 / num2 = 10
num1 — num2 = 45
=================================

Программа завершила работу!

Как видно, деление на 0 не произошло и программа не прекратила свою работу. Вместо этого мы увидели сообщение об ошибке в строке, где должна была произойти операция деления на 0.

Чтобы лучше понять, как именно значение, которое генерирует throw передается в catch, попробуйте заменить участок кода строк 22 — 33 на следующий код:

try 
{	
	if (num2 == 0)
	{
		throw "Ошибка - на 0 делить нельзя!!!!"; //генерировать символьную строку
	}
	cout << num1 / num2 << endl;	
}
catch(char *str)//сюда передастся строка
{
	cout << str << endl;
}

Тут при попытке деления на 0, throw генерирует не число, а символьную строку.  И  блоку catch в скобках мы показываем, что он будет принимать указатель на строку и ниже — выводить эту строку на экран. 

Исключение так же может генерироваться в определенной программистом функции. Например у нас определена функция деления двух целых чисел и в ней мы сразу проверим деление на 0.

int division(int n1, int n2)
{
	if (n2 == 0)
	{
		throw 99; 
	}
	return n1 / n2;
}

А вызывать эту функцию, конечно надо в try-блоке.

try 
{	
	cout << division(5, 0);
	cout << endl;	
}
catch(int i)
{
	cout << "Ошибка №" << i << " Деление на ноль." << endl; 
}

Деление на ноль не состоится и catch поймает число 99.

Что важно запомнить об исключениях:

  • try-блок — так называемый блок повторных попыток. В нем надо располагать код, который может привести к ошибке и аварийному закрытию программы;
  • throw генерирует исключение. То что остановит работу try-блока и приведет к выполнению кода catch-блока. Тип исключения должен соответствовать, типу принимаемого аргумента catch-блока;
  • catch-блок — улавливающий блок, поймает то, что определил  throw и выполнит свой код. Этот блок должен располагаться непосредственно под try-блоком. Никакой код не должен их разделять.
  • если в try-блоке исключение не генерировалось, catch-блок не сработает. Программа его обойдет.
Практика

К сожалению, для данной темы пока нет подходящих задач. Если у вас есть таковые на примете, отправте их по адресу: admin@cppstudio.com. Мы их опубликуем!

Автор: Marienko L.
Дата: 05.03.2014
Поделиться:

Комментарии

  1. flashk4

    Артём Кольченко

    Отвечу на предыдущий вопрос тогда здесь,под самим вопросам не получается написать.

    Здесь представлен простейший пример(деление на ноль) для понимания исключительной ситуации. На самом деле это довольно сложная тема,которая может занимать большую и трудную часть написания проекта.Это не только деление на ноль,но и так же потеря подключения к БД, ввод непредвиденных данных(т.е. вы написала программу по решению квадратного уравнения,а я вместо числовых коэффициентов возьму и введу ‘~’ и т.д.),проверка на существования файла и т.д. Одними условиями if … else тут не обойдёшься.

Оставить комментарий

Вы должны войти, чтобы оставить комментарий.