Infa (1)
.pdf
Создание" исключений
Когда возбуждается исключительная ситуация, программа просматривает стек функций до тех пор, пока не находит соответствующий catch. Если оператор catch не найден, STL будет обрабатывать исключение в стандартном обработчике, который делает все менее изящно, чем могли бы сделать вы, показывая какие-то непонятные (для конечного пользователя) сообщения и обычно аварийно завершая программу.
Однако более важным моментом является то, что пока просматривается стек функций, вызываются деструкторы всех локальных классов, так что вам не нужно забодиться об освобождении памяти и т.п.
Перегрузка глобальных операторов new/delete
А сейчас хотелось бы отправить вас к статье "Как обнаружить утечку памяти". В ней рассказывается, как обнаружить неправильное управление распределением памяти в вашей программе. Вы можете спросить, при чем тут перегрузка операторов? Если перегрузить стандартные new и delete, то открываются широкие возможности по отслеживанию ошибок (причем ошибок часто критических) с помощью исключений. Например:
char *a; try
{
a = new char[10];
}
catch (...){
//a не создан - обработать ошибку распределения памяти,
//выйти из программы и т.п.
}
// a успешно создан, продолжаем выполнение
Это, на первый взгляд, кажется длиннее, чем стандартная проверка в С "а равен NULL?", однако если в программе выделяется десяток динамических переменных, то такой метод оправдывает себя.
Операторы throw без параметров
Итак, мы увидели, как новый метод обработки ошибок удобен и прост. Блок try-catch может содержать вложенные блоки try-catch и если не будет определено соответствующего оператора catch на текущем уровен вложения, исключение будет поймано на более высоком уровне. Единственная вещь, о которой вы должны помнить, - это то, что операторы, следующие за throw, никогда не выполнятся.
try
{
throw;
//ни один оператор, следующий далее (до закрывающей скобки)
//выполнен не будет
}
catch(...)
{
cout << "Исключение!" << endl;
}
Такой метод может применяться в случаях, когда не нужно передавать никаких данных в блок catch.
Приложение
Приведем пример, как все вышеизложенное может быть использовано в конкретном приложении. Преположим, у вас в программе есть класс cMain и экземпляр этого класса Main: class cMain { public: bool Setup(); bool Loop(); // Основной цикл программы void Close(); }; cMain Main;
Ав функции main() или WinMain() вы можете использовать этот класс как-нибудь так:
try
{
Main.Setup();
Main.Loop();
Main.Close();
}
catch (Exception &e)
{
//использование класса, ведущего лог.
log("Exception thrown: %s", e.String());
// Показываем сообщение об ошибке и закрываем приложение.
}
Основной цикл программы может выглядеть примерно так:
while (AppActive)
{
try
{
// какие-то действия
}
catch (Exception &e)
{
/* Если исключение критическое, типа ошибки памяти, посылаем исключение дальше, в main(), оператором throw e; или просто throw.
Если исключение некритично, обрабатываем его и возвращаемся в основной цикл. */
}
}
Заключение
Метод обработки исключений, приведенный в статье, является удобным и мощным средством, однако только вам решать, использовать его или нет. Одно можно скачать точно - приведенный метод облегчит вам жизнь.
Всем нам интуитивно, чтопонятнотакое исключение. Это некое действие, кот силу, при наступлении нестандартнойГоворяситуациио об .сключениях в С++, ра
одну из таких нестандартных—делениеситуацийна 0. Представьте, что мы р программе, где ювручнувносим много числовых данныхрезультате.Далее, -вкакихто расчетов программы, значение одной из переменных0. А встанетследующейравнымстро кода это значение выступает делителем. Конечно,приведетэток днействиеизбежном закрытию программыодуи выхиз нееИвы. можете себе представить свое состо полчаса вводите данные, работая в этой программе,-то этапеи она какомпрост перестает работать и закрывается, а все данные пропадают.
В этой статье будет приведен оченьи короткийпростойпример, который поможе разобраться с исключениями пользователюС++. В нем будет предложено ввести ипрограмма проведет простые этимирасчетычислами. Ввод и расчеты будут определенное количествопомощьюраз циклаwhile.
Сначала наберите исходный код ниже и скомпилируйтеnum2 внесите.В переменнуючисло0, чтобы у вас было понимание той проблемы, что если в программе вс она резко прекращаетту, рабохотите вы этого или нет.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>; usingnamespacestd;
intmain()
{
setlocale(LC_ALL, "rus");
intnum1;
intnum2;
intvar = 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;
return0;
}
В программе цикл должен отработатьстрокедва12 мыразапередаем.В циклу значен переменнойvar и в этой же строке уменьшаем её на единицу операциюдекремента(var--). Но,если в переменнуюnum2 будет введено значение0, мы увидим только результатa сложения+ b программа завершит работу не зависи есть код ниже или нет, отработал цикл или нет.
Используя исключения, мы сможем избежатьЧтобытаких«пкрутить»проблем.исключение к этому примеру, надо познакомиться со следующимиthrow (вкомандамипереводе—С++: обработать,запустить),try (попытка),catch (поймать, ловить).В исходном код исключение сработает так:получаетпрограммаконкретное отуказаниепрограммиста—если значение определённой переменной в определённомtry-блоке)участке будеткода равно0, то в этом случае пусть генерируетсяthrow. исключениеЭтоисключение автоматически передастсяcatch-блоку в виде параметра и выполнитсягоблокакод. это
Сразу рассмотрим пример. А все подробные пояснения по исходному располагатьсяпод ним:
1
2
3#include <iostream>;
4usingnamespacestd;
5
6intmain()
7{
8 |
setlocale(LC_ALL, "rus"); |
|
|
||
9 |
intnum1; |
|
10 |
||
intnum2; |
||
11 |
||
intvar = 2; |
||
12 |
|
|
13 |
while(var--) |
|
14 |
{ |
|
15 |
cout << "Введите значение num1: "; |
|
cin >> num1; |
||
16 |
||
cout << "Введите значение num2: "; |
||
|
||
17 |
cin >> num2; |
|
18 |
|
|
19 |
cout << "num1 + num2 = "<< num1 + num2 << endl; |
|
20 |
cout << "num1 / num2 = "; |
|
|
||
21 |
try//код, который может привести к ошибке, располагается тут |
|
22 |
||
{ |
||
23 |
||
if(num2 == 0) |
||
24 |
{ |
|
25 |
throw123; //генерировать целое число 123 |
|
26 |
} |
|
cout << num1 / num2 << endl; |
||
27 |
||
} |
||
|
||
28 |
catch(inti)//сюдапередастсячисло 123 |
|
29 |
{ |
30 |
cout << "Ошибка №"<< i << " - на 0 делить нельзя!!!!"<< endl; |
|
} |
||
31 |
||
|
||
32 |
cout << "num1 - num2 = "<< num1 - num2 << endl; |
|
|
33cout << "================================="<< endl << endl;
34}
35
36 cout << "Программа завершила работу!"<< endl << endl;;
37
38return0;
39}
40
41
42
В строках 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 на следующий код:
1
2 |
try |
|
3 |
{ |
|
if(num2 == 0) |
||
4 |
||
{ |
||
5 |
||
throw"Ошибка - на 0 делить нельзя!!!!"; //генерировать символьную строк |
||
6 |
} |
7cout << num1 / num2 << endl;
8}
catch(char*str)//сюда передастся строка
9{
10cout << str << endl;
11}
12
Тут при попытке |
деления0, throw |
генерируетна не число, а символьнуюИ |
|||
|
|
||||
блоку |
catch |
в скобкахпоказываем,мы |
что он будет принимать указатель— на |
||
выводить строкуэту на |
экран. |
|
|||
1 |
intdivision(intn1, intn2) |
|
2 |
||
{ |
||
3 |
||
if(n2 == 0) |
||
4 |
{ |
|
5 |
throw99; |
Исключение так же может генерироваться в определенной прогр Например, у нас определена функция деления двух целых чисел и в ней деление на 0.
6 }
7 |
returnn1 / n2; |
|
} |
||
8 |
||
|
А вызывать эту функцию, конечноtry-блокенадо. в
1
2try
{
3cout << division(5, 0);
4cout << endl;
5}
6catch(inti)
7 |
{ |
|
cout<< "Ошибка №"<<i<< " Делениенаноль."<< endl; |
||
8 |
||
} |
||
|
||
9 |
|
Деление нольна не состоитсяcatch поймаети число99.
Что важно запомнить об исключениях:
try-блок— так называемый блок повторных попыток. В немкод,надо который может привести к ошибке и аварийному закрытию програ
throw генерирует исключениечто. остановитТо tryра-ботулока и приведет выполнению кодаcatch-блока. Тип исключения должен соответство принимаемого аргументаcatch-блока;
catch-блок— улавливающий блок, поймает то, чтоthrow иопредевыполнитил свой код. Этот блок располагатьсядолжен непосредственноtry-бл компод. Никакой ко не должен их разделять.
если tryв -блоке исключение не генерировалось,catch-блок сработает. Програ его обойдет.
