Функция ошибок c
Обработка исключений в C++
Введение
Язык С представляет программисту очень ограниченные возможности обработки исключений, возникших при работе программы. В этом отношении С++ намного развитее С. Здесь у программиста существенно большие возможности по непосредственной обработке исключений. Комитет по разработке стандартов С++ предоставил очень простую, но мощную форму обработки исключений.
Темные дни С
Типичная функция, написанная на С, выглядит примерно так:
Выглядит не очень, не так ли? Вы целиком и полностью зависите от значений, которые возвращают вам функции и для каждой ошибки вам постоянно нужен код, который ее обрабатывает. Если вы, скажем, в функции работаете хотя бы с 10 указателями (рапределяете память, освобождаете ее и т.д.), то наверняка половину кода функции будет занимать код обработки ошибок. Такая же ситуация будет в коде, вызывающем эту функцию, так как здесь также нужно обработать все возвращаемые коды ошибок.
Try-catch-throw
Давайте же разберем основы обработки исключений в С++. Чтобы комфортно работать с исключениями в С++ вам нужно знать лишь три ключевых слова:
- try (пытаться) — начало блока исключений;
- catch (поймать) — начало блока, «ловящего» исключение;
- throw (бросить) — ключевое слово, «создающее» («возбуждающее») исключение.
А теперь пример, демонстрирующий, как применить то, что вы узнали:
Если выполнить этот фрагмент кода, то мы получим следующий результат:
Теперь закоментируйте строку throw 1; и функция выдаст такой результат:
Как видите все очень просто, но если это применить с умом, такой подход покажется вам очень мощным средством обработки ошибок. Catch может «ловить» любой тип данных, так же как и throw может «кинуть» данные любого типа. Т.е. throw AnyClass(); будет правильно работать, так же как и catch (AnyClass &d) <>;.
Как уже было сказано, catch может «ловить» данные любого типа, но вовсе не обязательно при это указывать переменную. Т.е. прекрасно будет работать что-нибудь типа этого:
Так же можно «поймать» и все исключения:
Троеточие в этом случае показывает, что будут пойманы все исключения. При таком подходе нельзя указать имя переменной. В случае, если «кидаются» данные нестандартного типа (экземпляры определенных вами классов, структур и т.д.), лучше «ловить» их по ссылке, иначе вся «кидаемая» переменная будет скопирована в стек вместо того, чтобы просто передать указатель на нее. Если кидаются данные нескольких типов и вы хотите поймать конкретную переменную (вернее, переменную конкретного типа), то можно использовать несколько блоков catch, ловящих «свой» тип данных:
Создание» исключений
Когда возбуждается исключительная ситуация, программа просматривает стек функций до тех пор, пока не находит соответствующий catch. Если оператор catch не найден, STL будет обрабатывать исключение в стандартном обработчике, который делает все менее изящно, чем могли бы сделать вы, показывая какие-то непонятные (для конечного пользователя) сообщения и обычно аварийно завершая программу.
Однако более важным моментом является то, что пока просматривается стек функций, вызываются деструкторы всех локальных классов, так что вам не нужно забодиться об освобождении памяти и т.п.
Перегрузка глобальных операторов new/delete
А сейчас хотелось бы отправить вас к статье «Как обнаружить утечку памяти». В ней рассказывается, как обнаружить неправильное управление распределением памяти в вашей программе. Вы можете спросить, при чем тут перегрузка операторов? Если перегрузить стандартные new и delete, то открываются широкие возможности по отслеживанию ошибок (причем ошибок часто критических) с помощью исключений. Например:
Это, на первый взгляд, кажется длиннее, чем стандартная проверка в С «а равен NULL?», однако если в программе выделяется десяток динамических переменных, то такой метод оправдывает себя.
Операторы throw без параметров
Итак, мы увидели, как новый метод обработки ошибок удобен и прост. Блок try-catch может содержать вложенные блоки try-catch и если не будет определено соответствующего оператора catch на текущем уровен вложения, исключение будет поймано на более высоком уровне. Единственная вещь, о которой вы должны помнить, — это то, что операторы, следующие за throw, никогда не выполнятся.
Такой метод может применяться в случаях, когда не нужно передавать никаких данных в блок catch.
Приложение
Приведем пример, как все вышеизложенное может быть использовано в конкретном приложении. Преположим, у вас в программе есть класс cMain и экземпляр этого класса Main: class cMain < public: bool Setup(); bool Loop(); // Основной цикл программы void Close(); >; cMain Main;
А в функции main() или WinMain() вы можете использовать этот класс как-нибудь так:
Основной цикл программы может выглядеть примерно так:
Заключение
Метод обработки исключений, приведенный в статье, является удобным и мощным средством, однако только вам решать, использовать его или нет. Одно можно скачать точно — приведенный метод облегчит вам жизнь. Если хотите узнать об исключениях чуть больше, посмотрите публикацию Deep C++ на сервере MSDN.
Обработка ошибок в программах на Си
Хотя C не обеспечивает прямой поддержки обработки ошибок (или обработки исключений), существуют способы, с помощью которых обработка ошибок может быть осуществлена в C. Программист должен в первую очередь предотвращать ошибки и тестировать возвращаемые значения из функций.
Многие вызовы функций C возвращают -1 или NULL в случае ошибки, поэтому быстрое тестирование этих возвращаемых значений легко выполнить, например, с помощью оператора if. Например, в Socket Programming возвращаемое значение функций, таких как socket (), listen () и т. Д., Проверяется на наличие ошибки или нет.
Пример: обработка ошибок в программировании сокетов
Различные методы обработки ошибок в C
- Глобальная переменная errno: когда в C вызывается функция, переменной с именем errno автоматически присваивается код (значение), который можно использовать для определения типа возникшей ошибки. Это глобальная переменная, указывающая, что ошибка произошла во время любого вызова функции и определена в заголовочном файле errno.h.
Разные коды (значения) для errno означают разные типы ошибок. Ниже приведен список из нескольких различных значений errno и соответствующего значения:
// Реализация C, чтобы увидеть, как значение errno
// установить в случае любой ошибки в C
#include
#include
// Если файл открыт, который не существует,
// тогда это будет ошибка и соответствующая
// значение errno будет установлено
// открываем файл который
fp = fopen ( «GeeksForGeeks.txt» , «r» );
printf ( » Value of errno: %dn » , errno );
Примечание. Здесь значение errno установлено в 2, что означает — Нет такого файла или каталога. В онлайн-среде IDE может выдаваться ошибка № 13, в которой говорится, что разрешение отклонено.
Если требуется показать описание ошибки, то есть две функции, которые можно использовать для отображения текстового сообщения, связанного с errorno. Функции:
- perror: отображает строку, которую вы передаете ей, затем двоеточие, пробел, а затем текстовое представление текущего значения errno.
Синтаксис: - strerror (): возвращает указатель на текстовое представление текущего значения errno.
Синтаксис:
// Реализация C, чтобы увидеть, как perror () и strerror ()
// функции используются для печати сообщений об ошибках.
#include
#include
#include
// Если файл открыт, который не существует,
// тогда это будет ошибка и соответствующая
// значение errno будет установлено
fp = fopen ( » GeeksForGeeks.txt » , «r» );
// открываем файл который
printf ( «Value of errno: %dn » , errno );
printf ( «The error message is : %sn» ,
perror ( «Message from perror» );
Выход:
На личном рабочем столе:
В онлайн IDE:
Примечание . Функция perror () отображает переданную ей строку, за которой следует двоеточие и текстовое сообщение с текущим значением errno.
Состояние выхода: Стандарт C определяет две константы: EXIT_SUCCESS и EXIT_FAILURE, которые могут быть переданы в exit () для указания успешного или неудачного завершения, соответственно. Это макросы, определенные в stdlib.h.
// C реализация, которая показывает
// использование EXIT_SUCCESS и EXIT_FAILURE.
#include
#include
#include
#include
C++ — Урок 011. Исключения
Что такое исключение? Это ситуация, которая не предусмотрена стандартным поведением программы. Например, попытка доступа к элементу в классе Vector (который мы разбирали в статье про классы ), который не существует. То есть происходит выход за пределы вектора. В данном случае можно воспользоваться исключениями, чтобы прервать выполнение программы. Это необходимо потому, что
- Как правило в таких случаях, автор класса Vector не знает, как пользователь захочет использовать его класс, а также не знает в какой программе этот класс будет использоваться.
- Пользователь класса Vector не может всегда контролировать правильность работы этого класса, поэтому ему нужно сообщить о том, что что-то пошло не так.
Для разрешения таких ситуация в C++ можно использовать технику исключений.
Рассмотрим, как написать вызов исключения в случае попытки доступа к элементу по индексу, который не существует в классе Vector.
Здесь применяется исключение out_of_range. Данное исключение определено в заголовочном файле .
Оператор throw передаёт контроль обработчику для исключений типа out_of_range в некоторой функции, которая прямо или косвенно вызывает Vector::operator[]() . Для того, чтобы обработать исключения необходимо воспользоваться блоком операторов try catch.
Инварианты
Также блоки try catch позволяют производить обработку нескольких различных исключений, что вносит инвариантность в работу механизма исключений C++.
Например, класс вектор при создании может получить неправильный размер вектора или не найти свободную память для элементов, которые он будет содержать.
Данный конструктор может выбросить исключение в двух случаях:
- Если в качестве аргумента size будет передано отрицательное значение
- Если оператор new не сможет выделить память
length_error — это стандартный оператор исключений, поскольку библиотека std часто использует данные исключения при своей работе.
Обработка исключений будет выглядеть следующим образом:
Также можно выделить свои собственные исключения.
Виды исключений
Все исключения стандартной библиотеки наследуются от std::exception.
На данный момент существуют следующие виды исключений:
- logic_error
- invalid_argument
- domain_error
- length_error
- out_of_range
- future_error (C++11)
- runtime_error
- range_error
- overflow_error
- underflow_error
- system_error (C++11)
- ios_base::failure (начиная с C++11)
- bad_typeid
- bad_cast
- bad_weak_ptr (C++11)
- bad_function_call (C++11)
- bad_alloc
- bad_array_new_length (C++11)
- bad_exception
- ios_base::failure (до C++11)
std::logic_error
Исключение определено в заголовочном файле
Определяет тип объекта, который будет брошен как исключение. Он сообщает об ошибках, которые являются следствием неправильной логики в рамках программы, такие как нарушение логической предпосылки или класс инвариантов, которые возможно предотвратить.
Этот класс используется как основа для ошибок, которые могут быть определены только во время выполнения программы.
std::invalid_argument
Исключение определено в заголовочном файле
Наследован от std::logic_error. Определяет исключение, которое должно быть брошено в случае неправильного аргумента.
Например, на MSDN приведён пример, когда в объект класса bitset из стандартной библиотеки
В данном примере передаётся неправильная строка, внутри которой имеется символ ‘b’, который будет ошибочным.
std::domain_error
Исключение определено в заголовочном файле
Наследован от std::logic_error. Определяет исключение, которое должно быть брошено в случае если математическая функция не определена для того аргумента, который ей передаётся, например:
std::length_error
Исключение определено в заголовочном файле
Наследован от std::logic_error. Определяет исключение, которое должно быть броше в том случае, когда осуществляется попытка реализации превышения допустим пределов для объекта. Как это было показано для размера вектора в начале статьи.
std::out_of_range
Исключение определено в заголовочном файле
Наследован от std::logic_error. Определяет исключение, которое должно быть брошено в том случае, когда происходит выход за пределы допустимого диапазона значений объекта. Как это было показано для диапазона значений ветора в начале статьи.
std::future_error
Исключение определено в заголовочном файле
Наследован от std::logic_error. Данное исключение может быть выброшено в том случае, если не удалось выполнить функцию, которая работает в асинхронном режиме и зависит от библиотеки потоков. Это исключение несет код ошибки совместимый с std::error_code .
std::runtime_error
Исключение определено в заголовочном файле
Является базовым исключением для исключений, которые не могут быть легко предсказаны и должны быть брошены во время выполнения программы.
std::range_error
Исключение определено в заголовочном файле
Исключение используется при ошибках при вычислении значений с плавающей запятой, когда компьютер не может обработать значение, поскольку оно является либо слишком большим, либо слишком маленьким. Если значение является значение интегрального типа, то должны использоваться исключения underflow_error или overflow_error .
std::overflow_error
Исключение определено в заголовочном файле
Исключение используется при ошибках при вычислении значений с плавающей запятой интегрального типа, когда число имеет слишком большое положительное значение, положительную бесконечность, при которой происходит потеря точности, т.е. результат настолько большой, что не может быть представлен числом в формате IEEE754.
std::underflow_error
Исключение определено в заголовочном файле
Исключение используется при ошибках при вычислении значений с плавающей запятой интегрального типа, при которой происходит потеря точности, т.е. результат настолько мал, что не может быть представлен числом в формате IEEE754.
std::system_error
Исключение определено в заголовочном файле
std::system_error — это тип исключения, которое вызывается различными функциями стандартной библиотеки (как правило, функции, которые взаимодействуют с операционной системой, например, конструктор std::thread ), при этом исключение имеет соответствующий std::error_code .
std::ios_base::failure
Исключение определено в заголовочном файле
Отвечает за исключения, которые выбрасываются при ошибках функций ввода вывода.
std::bad_typeid
Исключение определено в заголовочном файле
Исключение этого типа возникает, когда оператор typeid применяется к нулевому указателю полиморфного типа.
std::bad_cast
Исключение определено в заголовочном файле
Данное исключение возникает в том случае, когда производится попытка каста объекта в тот тип объекта, который не входит с ним отношения наследования.
std::bad_weak_ptr
Исключение определено в заголовочном файле
std::bad_weak_ptr – тип объекта, генерируемый в качестве исключения конструкторами std::shared_ptr , которые принимают std::weak_ptr в качестве аргумента, когда std::weak_ptr ссылается на уже удаленный объект.
std::bad_function_call
Исключение определено в заголовочном файле
Данное исключение генерируется в том случае, если был вызван метод std::function::operator() объекта std::function , который не получил объекта функции, то есть ему был передан в качестве инициализатора nullptr, например, а объект функции так и не был передан.
std::bad_alloc
Исключение определено в заголовочном файле
Вызывается в том случае, когда не удаётся выделить память.
std::bad_array_new_length
Исключение определено в заголовочном файле
Исключение вызывается в следующих случаях:
- Массив имеет отрицательный размер
- Общий размер нового массива превысил максимальное значение, определяемое реализацией
- Количество элементов инициализации превышает предлагаемое количество инициализирующих элементов
std::bad_exception
Исключение определено в заголовочном файле
std::bad_exception — это тип исключения в C++, которое выполняется в следующих ситуациях:
- Если нарушается динамическая спецификация исключений
- Если std::exception_ptr хранит копию пойманного исключения, и если конструктор копирования объекта исключения поймал current_exception, тогда генерируется исключение захваченных исключений.
Рекомендуем хостинг TIMEWEB
Рекомендуемые статьи по этой тематике
По статье задано1 вопрос(ов)
Функция ошибок c
(C) Dale, 01.02.2011 — 02.02.2011.
double triangleArea ( double a , double b , double c )
<
double p = ( a + b + c ) / 2.0 ;
double result = sqrt ( p * ( p — a ) * ( p — b ) * ( p — c ) ) ;
return result ;
>
double triangleArea ( double a , double b , double c )
<
if ( ( a = ( b + c ) )
)
; // do something?
double p = ( a + b + c ) / 2.0 ;
double result = sqrt ( p * ( p — a ) * ( p — b ) * ( p — c ) ) ;
return result ;
>
double triangleArea ( double a , double b , double c )
<
if ( ( a = ( b + c ) )
)
return — 1.0 ;
double p = ( a + b + c ) / 2.0 ;
double result = sqrt ( p * ( p — a ) * ( p — b ) * ( p — c ) ) ;
return result ;
>
double triangleArea ( double a , double b , double c )
<
if ( ( a = ( b + c ) )
)
exit ( EXIT_FAILURE ) ;
double p = ( a + b + c ) / 2.0 ;
double result = sqrt ( p * ( p — a ) * ( p — b ) * ( p — c ) ) ;
return result ;
>
.
// Защищенный блок
Try
<
.
потенциально_опасный код
if (возникла_ошибка)
Throw(e1); // e1 — код конкретной ошибки
.
вызов_потенциально_опасной_функции()
.
>
// Сюда мы попадаем только в случае возникновения ошибки
Catch(e) // здесь e — код возникшей ошибки
<
.
код_обработки_ошибки
.
>
// Конец защищенного блока
.
потенциально_опасная_функция()
<
.
if (возникла_ошибка)
Throw(e2); // e2 — код конкретной ошибки
.
>
enum ERRORCODE_T
<
NEGATIVE_SIDE ,
BAD_TRIANGLE
> ;
extern double triangleArea ( double a , double b , double c ) ;
#include
#include
#include «CException.h»
#include «ErrorCode.h»
#include «TriangleArea.h»
double triangleArea ( double a , double b , double c )
<
if ( ( a = ( b + c ) )
)
Throw ( BAD_TRIANGLE ) ;
double p = ( a + b + c ) / 2.0 ;
double result = sqrt ( p * ( p — a ) * ( p — b ) * ( p — c ) ) ;
return result ;
>
#include
#include
#include «TriangleArea.h»
#include «CException.h»
#include «ErrorCode.h»
int main ( int argc , char * argv [ ] )
<
CEXCEPTION_T e ;
double a , b , c ;
a = 10.0 ;
b = 2.0 ;
c = 2.0 ;
printf ( «a=%f b=%f c=%f n » , a , b , c ) ;
Try
<
double area = triangleArea ( a , b , c ) ;
printf ( «area=%f n » , area ) ;
>
Catch ( e )
<
switch ( e )
<
case NEGATIVE_SIDE :
printf ( «One of triangle sides is negative. n » ) ;
break ;
case BAD_TRIANGLE :
printf ( «Triangle cannot be made of these sides. n » ) ;
break ;
default :
printf ( «Unknown error: %d n » , e ) ;
>
>
system ( «PAUSE» ) ;
return 0 ;
>
Обработка исключений в C++
Введение
Язык С представляет программисту очень ограниченные возможности обработки исключений, возникших при работе программы. В этом отношении С++ намного развитее С. Здесь у программиста существенно большие возможности по непосредственной обработке исключений. Комитет по разработке стандартов С++ предоставил очень простую, но мощную форму обработки исключений.
Темные дни С
Типичная функция, написанная на С, выглядит примерно так:
Выглядит не очень, не так ли? Вы целиком и полностью зависите от значений, которые возвращают вам функции и для каждой ошибки вам постоянно нужен код, который ее обрабатывает. Если вы, скажем, в функции работаете хотя бы с 10 указателями (рапределяете память, освобождаете ее и т.д.), то наверняка половину кода функции будет занимать код обработки ошибок. Такая же ситуация будет в коде, вызывающем эту функцию, так как здесь также нужно обработать все возвращаемые коды ошибок.
Try-catch-throw
Давайте же разберем основы обработки исключений в С++. Чтобы комфортно работать с исключениями в С++ вам нужно знать лишь три ключевых слова:
- try (пытаться) — начало блока исключений;
- catch (поймать) — начало блока, «ловящего» исключение;
- throw (бросить) — ключевое слово, «создающее» («возбуждающее») исключение.
А теперь пример, демонстрирующий, как применить то, что вы узнали:
Если выполнить этот фрагмент кода, то мы получим следующий результат:
Теперь закоментируйте строку throw 1; и функция выдаст такой результат:
Как видите все очень просто, но если это применить с умом, такой подход покажется вам очень мощным средством обработки ошибок. Catch может «ловить» любой тип данных, так же как и throw может «кинуть» данные любого типа. Т.е. throw AnyClass(); будет правильно работать, так же как и catch (AnyClass &d) <>;.
Как уже было сказано, catch может «ловить» данные любого типа, но вовсе не обязательно при это указывать переменную. Т.е. прекрасно будет работать что-нибудь типа этого:
Так же можно «поймать» и все исключения:
Троеточие в этом случае показывает, что будут пойманы все исключения. При таком подходе нельзя указать имя переменной. В случае, если «кидаются» данные нестандартного типа (экземпляры определенных вами классов, структур и т.д.), лучше «ловить» их по ссылке, иначе вся «кидаемая» переменная будет скопирована в стек вместо того, чтобы просто передать указатель на нее. Если кидаются данные нескольких типов и вы хотите поймать конкретную переменную (вернее, переменную конкретного типа), то можно использовать несколько блоков catch, ловящих «свой» тип данных:
Создание» исключений
Когда возбуждается исключительная ситуация, программа просматривает стек функций до тех пор, пока не находит соответствующий catch. Если оператор catch не найден, STL будет обрабатывать исключение в стандартном обработчике, который делает все менее изящно, чем могли бы сделать вы, показывая какие-то непонятные (для конечного пользователя) сообщения и обычно аварийно завершая программу.
Однако более важным моментом является то, что пока просматривается стек функций, вызываются деструкторы всех локальных классов, так что вам не нужно забодиться об освобождении памяти и т.п.
Перегрузка глобальных операторов new/delete
А сейчас хотелось бы отправить вас к статье «Как обнаружить утечку памяти». В ней рассказывается, как обнаружить неправильное управление распределением памяти в вашей программе. Вы можете спросить, при чем тут перегрузка операторов? Если перегрузить стандартные new и delete, то открываются широкие возможности по отслеживанию ошибок (причем ошибок часто критических) с помощью исключений. Например:
Это, на первый взгляд, кажется длиннее, чем стандартная проверка в С «а равен NULL?», однако если в программе выделяется десяток динамических переменных, то такой метод оправдывает себя.
Операторы throw без параметров
Итак, мы увидели, как новый метод обработки ошибок удобен и прост. Блок try-catch может содержать вложенные блоки try-catch и если не будет определено соответствующего оператора catch на текущем уровен вложения, исключение будет поймано на более высоком уровне. Единственная вещь, о которой вы должны помнить, — это то, что операторы, следующие за throw, никогда не выполнятся.
Такой метод может применяться в случаях, когда не нужно передавать никаких данных в блок catch.
Приложение
Приведем пример, как все вышеизложенное может быть использовано в конкретном приложении. Преположим, у вас в программе есть класс cMain и экземпляр этого класса Main: class cMain < public: bool Setup(); bool Loop(); // Основной цикл программы void Close(); >; cMain Main;
А в функции main() или WinMain() вы можете использовать этот класс как-нибудь так:
Основной цикл программы может выглядеть примерно так:
Заключение
Метод обработки исключений, приведенный в статье, является удобным и мощным средством, однако только вам решать, использовать его или нет. Одно можно скачать точно — приведенный метод облегчит вам жизнь. Если хотите узнать об исключениях чуть больше, посмотрите публикацию Deep C++ на сервере MSDN.