Студия web-дизайна Хостмэйк
Наши работыКонтактыО компанииОтзывыГлоссарийСтатьи

Обработка исключений в C++

Статьи Программирование на С/С++

Введение 

Язык С представляет программисту очень ограниченные возможности обработки исключений, возникших при работе программы. В этом отношении С++ намного развитее С. Здесь у программиста существенно большие возможности по непосредственной обработке исключений. Комитет по разработке стандартов С++ предоставил очень простую, но мощную форму обработки исключений.Темные дни С 

Типичная функция, написанная на С, выглядит примерно так: 

long DoSomething() 



long *a, c; 

FILE *b; 

a = malloc(sizeof(long) * 10); 

if (a == NULL) 

return 1; 

b = fopen("something.bah", "rb"); 

if (b == NULL) { 

free(a); 

return 2; 



fread(a, sizeof(long), 10, b); 

if (a[0] != 0x10) { 

free(a); 

fclose(b); 

return 3; 



fclose(b); 

c = a[1]; 

free(a); 

return c; 

}


Выглядит не очень, не так ли? Вы целиком и полностью зависите от значений, которые возвращают вам функции и для каждой ошибки вам постоянно нужен код, который ее обрабатывает. Если вы, скажем, в функции работаете хотя бы с 10 указателями (рапределяете память, освобождаете ее и т.д.), то наверняка половину кода функции будет занимать код обработки ошибок. Такая же ситуация будет в коде, вызывающем эту функцию, так как здесь также нужно обработать все возвращаемые коды ошибок.Try-catch-throw 

Давайте же разберем основы обработки исключений в С++. Чтобы комфортно работать с исключениями в С++ вам нужно знать лишь три ключевых слова:



  • try (пытаться) - начало блока исключений;

  • catch (поймать) - начало блока, "ловящего" исключение;

  • throw (бросить) - ключевое слово, "создающее" ("возбуждающее") исключение.


А теперь пример, демонстрирующий, как применить то, что вы узнали:


void func() 



try 



throw 1; 



catch(int a) 



cout << "Caught exception number: " << a << endl; 

return; 



cout << "No exception detected!" << endl; 

return; 

}


Если выполнить этот фрагмент кода, то мы получим следующий результат:


Caught exception number: 1


Теперь закоментируйте строку throw 1; и функция выдаст такой результат:


No exception detected!


Как видите все очень просто, но если это применить с умом, такой подход покажется вам очень мощным средством обработки ошибок. Catch может "ловить" любой тип данных, так же как и throw может "кинуть" данные любого типа. Т.е. throw AnyClass(); будет правильно работать, так же как и catch (AnyClass &d) {}; .Как уже было сказано, catch может "ловить" данные любого типа, но вовсе не обязательно при это указывать переменную. Т.е. прекрасно будет работать что-нибудь типа этого:


catch(dumbclass) { }


так же, как и


catch(dumbclass&) { }


Так же можно "поймать" и все исключения:


catch(...) { }


Троеточие в этом случае показывает, что будут пойманы все исключения. При таком подходе нельзя указать имя переменной. В случае, если "кидаются" данные нестандартного типа (экземпляры определенных вами классов, структур и т.д.), лучше "ловить" их по ссылке, иначе вся "кидаемая" переменная будет скопирована в стек вместо того, чтобы просто передать указатель на нее. Если кидаются данные нескольких типов и вы хотите поймать конкретную переменную (вернее, переменную конкретного типа), то можно использовать несколько блоков catch, ловящих "свой" тип данных:


try { 

throw 1; 

// throw 'a'; 



catch (long b) { 

cout << "пойман тип long: " << b << endl; 



catch (char b) { 

cout << "пойман тип char: " << b << endl; 

}


"Создание" исключений 

Когда возбуждается исключительная ситуация, программа просматривает стек функций до тех пор, пока не находит соответствующий 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. 

Если исключение некритично, обрабатываем его и 

возвращаемся в основной цикл. */ 



}


Заключение 

Метод обработки исключений, приведенный в статье, является удобным и мощным средством, однако только вам решать, использовать его или нет. Одно можно скачать точно - приведенный метод облегчит вам жизнь. Если хотите узнать об исключениях чуть больше, посмотрите публикацию Deep C++ на сервере
MSDN.


Источник: http://realcoding.net

16.02.2006

Телефон

+7 8636 237-836

Поиск

VSESMI.ru — новости в СМИ.
Один из больших по объему информации проектов, работающих под управлением HostCMS.

Tur-Hotel.ru — отзывы об отелях
На сайте представлено описание отелей, рейтинг отелей с отзывами туристов.