Перехват исключений в Visual C++
В этой статье описывается try-catch-finally использование блока для перехвата исключения.
Исходная версия продукта: Visual C++
Исходный номер базы знаний: 815662
Аннотация
Блок try-catch-finally — это оболочка, которая помещается вокруг любого кода, в котором может возникнуть исключение. Перехват и работа с исключениями — это стандартные задачи программирования.
Блок try-catch-finally состоит из следующих разделов:
- Любой код, который может вызвать исключение, помещается в блок try.
- Если возникает исключение, catch вводится блок, и программа может выполнить соответствующую операцию для восстановления или оповещения пользователя.
- Код в блоке finally всегда выполняется и может выполнять очистку после возникновения исключения. Блок finally необязателен.
В этой статье рассматриваются следующие пространства имен библиотеки классов Microsoft платформа .NET Framework: System.IO и System.Security .
Перехват исключений в Visual C++ .NET
- Запустите Visual Studio .NET.
- В меню Файл выберите пункт Создать и затем пункт Проект.
- В Visual C++ щелкните Visual C++ в разделе Типы проектов, а затем выберите консольное приложение CLR в разделе Шаблоны.
- В поле Имя введите Q815662 и нажмите кнопку ОК.
- Замените весь код в окне кода Q815662.cpp следующим кодом. Код объявляет и инициализирует три переменные. Инициализация k вызывает ошибку.
#include "stdafx.h" #using #include using namespace System; void _tmain(void) < Console::WriteLine("We're going to divide 10 by 0 and see what happens. "); Console::WriteLine(); int i = 10; int j = 0; int k = i/j; //Error on this line. >
#include "stdafx.h" #using #include using namespace System; void _tmain(void) < try < Console::WriteLine("We're going to divide 10 by 0 and see what happens. "); Console::WriteLine(); int i = 10; int j = 0; int k = i/j; //Error on this line. >catch(. ) < Console::WriteLine("An error occurred."); >>
Примечание. Сообщение об ошибке catch из блока отображается вместо сообщения об ошибке системного исключения.
This statement is always printed.
Замените код в окне кода Q815662.cpp следующим кодом:
#include "stdafx.h" #using #include using namespace System; void _tmain(void) < try < Console::WriteLine("We're going to divide 10 by 0 and see what happens. "); Console::WriteLine(); int i = 10; int j = 0; int k = i/j; //Error on this line. >catch(. ) < Console::WriteLine("An error occurred."); >__finally //This section is performed regardless of the above processing. < Console::WriteLine(); Console::WriteLine("This statement is always printed"); >>
#include "stdafx.h" #using #include using namespace System; using namespace System::Reflection; void _tmain(void) < try < Console::WriteLine("We're going to divide 10 by 0 and see what happens. "); Console::WriteLine(); int i = 10; int j = 0; int k = i/j; //Error on this line. >catch(Exception *e) < Console::WriteLine("An error occurred."); Console::WriteLine(e->Message); // Print the error message. Console::WriteLine(e->StackTrace); //String that contains the stack trace for this exception. > __finally //This section is performed regardless of the above processing. < Console::WriteLine(); Console::WriteLine("This statement is always printed"); >Console::ReadLine(); >
#include "stdafx.h" #using #include using namespace System; using namespace System::IO; using namespace System::Security; void _tmain(void) < try < File::Create("c:\\temp\\testapp.txt"); //Can fail for a number of resons >// This error may occur if the temp folder does not exist. catch(IOException *ioe) < Console::WriteLine("An IOException exception occurred!"); Console::WriteLine(ioe->ToString()); > // You do not have the appropriate permission to take this action. catch(SecurityException *se) < Console::WriteLine("An SecurityException exception occur") >// Catch all exceptions catch(Exception *e) < Console::WriteLine(e->ToString()); > >
Обратная связь
Были ли сведения на этой странице полезными?
Исключения
В процессе работы программы могут возникать различные ошибки. Например, при передаче файла по сети оборвется сетевое подключение или будут введены некорректные и недопустимые данные, которые вызовут падение программы. Такие ошибки еще называются исключениями. Исключение представлякт временный объект любого типа, который используется для сигнализации об ошибке. Цель объекта-исключения состоит в том, чтобы передать информацию из точки, в которой произошла ошибка, в код, который должен ее обработать. Если исключение не обработано, то при его возникновении программа прекращает свою работу.
Например, в следующей программе происходит деление чисел:
#include double divide(int a, int b) < return a / b; >int main() < int x; int y<>; double z
Эта программа успешно скомпилируется, но при ее выполнении возникнет ошибка, поскольку в коде производится деление на ноль, после чего программа аварийно завершится.
С одной стороны, мы можем в функции divide определить проверку и выполнять деление, если параметр b не равен 0. Однако нам в любом случае надо возвращать из функции divide некоторый результат — некоторое число. То есть мы не можем просто написать:
double divide(int a, int b) < if (b) return a / b; else std::cout
И в этом случае нам надо известить систему о возникшей ошибке. Для этого используется оператор throw .
Оператор throw генерирует исключение. Через оператор throw можно передать информацию об ошибке. Например, функция divide могла бы выглядеть следующим образом:
double divide(int a, int b) < if (b) return a / b; throw "Division by zero!"; >
То есть если параметр b равен 0, то генерируем исключение.
Но это исключение еще надо обработать в коде, где будет вызываться функция divide. Для обработки исключений применяется конструкция try. catch . Она имеет следующую форму:
try < инструкции, которые могут вызвать исключение >catch(объявление_исключения)
В блок кода после ключевого слова try помещается код, который потенциально может сгенерировать исключение.
После ключевого слова catch в скобках идет параметр, который передает информацию об исключении. Затем в блоке производится собственно обработка исключения.
Так изменим весь код следующим образом:
#include double divide(int a, int b) < if (b) return a / b; throw "Division by zero!"; >int main() < int x; int y<>; try < double z Код, который потенциально может сгенерировать исключение — вызов функции divide помещается в блок try. В блоке catch идет обработка исключения. Причем многоточие в скобках после оператора catch ( catch(. ) ) позволяет обработать любое исключение. В итоге когда выполнение программы дойдет до строки При выполнении этой строки будет сгенерировано исключение, поэтому последующие инструкции из блока try выполняться не будут, а управление перейдет в блок catch, в котором на консоль просто выводится сообщение об ошибке. После выполнения блока catch программа аварийно не завершится, а продолжит свою работу, выполняя операторы после блока catch: Однако в данном случае мы только знаем, что произошла какая-то ошибка, а какая именно, неизвестно. Поэтому через параметр в блоке catch мы можем получить то сообщение, которое передается оператору throw : #include >iostream < double divide(int a, int b) < if (b) return a / b; throw "Division by zero!"; >int main() < int x; int y<>; try < double z С помощью параметра const char* error_message получаем сообщение, которое предано оператору throw, и выводим это сообщение на консоль. Почему здесь мы получаем сообщение об ошибке в виде типа const char* ? Потому что после оператора throw идет строковый литерал, который представляет как раз тип const char* . И в этом случае консольный вывод будет выглядеть следующим образом: Таким образом, мы можем узнать суть возникшего исключения. Подобным образом мы можем передавать информацию об исключении через любые типы, например, std::string: Тогда в блоке catch мы можем получить эту информацию в виде объекта std::string : catch (std::string error_message) Если же исключение не обработано, то вызывается функция std::terminate() (из модуля стандартной библиотеки C++), которая, в свою очередь, по умолчанию вызывает другую функцию — std::abort() (из ), которая собственно и завершает программу. Существует очень много функций и в стандартной библиотеке С++, и в каких-то сторонних библиотеках. И может возникнуть вопрос, какие из них вызывать в конструкции try-catch, чтобы не столкнуться с необработанным исключением и аварийным завершением программы. В этом случае может помочь прежде всего документация по функции (при ее наличии). Другой сигнал — ключевое слово noexcept , которое при использовании в заголовке функции указывает, что эта функция никогда не будет генерировать исключения. Например: Здесь указываем, что функция print() никогда не вызовет исключение. Таким образом, встретив функцию с подобным ключевым словом, можно ожидать, что она не вызовет исключения. И соответственно нет необходимости помещать ее вызов в конструкцию try-catch. При обработке исключения стоит помнить, что при передаче объекта оператору throw блок catch получает копию этого объекта. И эта копия существует только в пределах блока catch. Для значений примитивных типов, например, int , копирование значения может не влиять на производительность программы. Однако при передаче объектов классов издержки могут выше. Поэтому в этом случае объекты обычно передаются по ссылке, например: #include double divide(int a, int b) < if (b) return a / b; throw std::string; > int main() < int x; int y<>; try < double z Мы можем генерировать и обрабатывать несколько разных исключительных ситуаций. Допустим, нам надо, чтобы при делении делитель (второе число) был не больше, чем делимое (первое число): В функции divide в зависимости от значения числа b оператору throw передаем либо число: либо строковый литерал: Для тестирования функции divide определена другая функция — test, где вызов функции divide() помещен в конструкцию try..catch . Поскольку при генерации исключения мы можем получать ошибку в виде двух типов — int (если b равно 0) и const char* (если b больше a), то для обработки каждого типа исключений определены два разных блока catch: catch (int code) < std::cout catch (const char* error_message)
В функции main вызываем функцию test, передавая в нее различные числа. При вызове: число b не равно 0 и меньше a, поэтому никаких исключений не возникает, блок try срабатывает до конца, и функция завершает свое выполнение. При втором вызове число b равно 0, поэтому генерируется исключение, а в качестве объекта исключения передается число 0. Поэтому при возникновении исключения программа выберет тот блок catch, где обрабатывается исключения типа int: catch (int code) При третьем вызове число b больше a, поэтому объект исключения будет представлять строковый литерал или const char* . Поэтому при возникновении исключения программа выберет блок catch, где обрабатывается исключения типа const char*: catch (const char* error_message) Таким образом, в данном случае мы получим следующий консольный вывод: Может быть ситуация, когда генерируется исключение внутри конструкции try-catch , и даже есть блок catch для обработки исключений, однако он обрабатывает другие типы исключений: Здесь нет блока catch для обработки исключения типа int . Поэтому при генерации исключения: Программа не найдет нужный блок catch для обработки исключения, и программа аварийно завершит свое выполнение. Стоит отметить, что, если в блоке try создаются некоторые объекты, то при возникновении исключения у них вызываются деструкторы. Например: В классе Person определяет деструктор, который выводит сообщение на консоль. В функции print просто генерируем исключение. В функции main в блоке try создаем один объект Person и вызываем у него функцию print, что естественно приведет к генерарции исключения и переходу управления программы в блок catch . И если мы посмотрим на консольный вывод то мы увидим, что прежде чем начнется обработка исключения в блоке catch, будет вызван деструктор объекта Person. Argument ‘Topic id’ is null or empty Сейчас на форуме © Николай Павлов, Planetaexcel, 2006-2023 Использование любых материалов сайта допускается строго с указанием прямой ссылки на источник, упоминанием названия сайта, имени автора и неизменности исходного текста и иллюстраций. Иногда при выполнении программы возникают ошибки, которые трудно предусмотреть или предвидеть, а иногда и вовсе невозможно. Например, при передачи файла по сети может неожиданно оборваться сетевое подключение. такие ситуации называются исключениями . Язык C# предоставляет разработчикам возможности для обработки таких ситуаций. Для этого в C# предназначена конструкция try. catch. finally . try < >catch < >finally При использовании блока try. catch..finally вначале выполняются все инструкции в блоке try . Если в этом блоке не возникло исключений, то после его выполнения начинает выполняться блок finally . И затем конструкция try..catch..finally завершает свою работу. Если же в блоке try вдруг возникает исключение, то обычный порядок выполнения останавливается, и среда CLR начинает искать блок catch , который может обработать данное исключение. Если нужный блок catch найден, то он выполняется, и после его завершения выполняется блок finally. Если нужный блок catch не найден, то при возникновении исключения программа аварийно завершает свое выполнение. Рассмотрим следующий пример: В данном случае происходит деление числа на 0, что приведет к генерации исключения. И при запуске приложения в режиме отладки мы увидим в Visual Studio окошко, которое информирует об исключении: В этом окошке мы видим, что возникло исключение, которое представляет тип System.DivideByZeroException , то есть попытка деления на ноль. С помощью пункта View Details можно посмотреть более детальную информацию об исключении. И в этом случае единственное, что нам остается, это завершить выполнение программы. Чтобы избежать подобного аварийного завершения программы, следует использовать для обработки исключений конструкцию try. catch. finally . Так, перепишем пример следующим образом: В данном случае у нас опять же возникнет исключение в блоке try, так как мы пытаемся разделить на ноль. И дойдя до строки выполнение программы остановится. CLR найдет блок catch и передаст управление этому блоку. После блока catch будет выполняться блок finally. Таким образом, программа по-прежнему не будет выполнять деление на ноль и соответственно не будет выводить результат этого деления, но теперь она не будет аварийно завершаться, а исключение будет обрабатываться в блоке catch. Следует отметить, что в этой конструкции обязателен блок try . При наличии блока catch мы можем опустить блок finally: try < int x = 5; int y = x / 0; Console.WriteLine($"Результат: "); > catch И, наоборот, при наличии блока finally мы можем опустить блок catch и не обрабатывать исключение: try < int x = 5; int y = x / 0; Console.WriteLine($"Результат: "); > finally Однако, хотя с точки зрения синтаксиса C# такая конструкция вполне корректна, тем не менее, поскольку CLR не сможет найти нужный блок catch, то исключение не будет обработано, и программа аварийно завершится. Ряд исключительных ситуаций может быть предвиден разработчиком. Например, пусть в программе есть метод, который принимает строку, конвертирует ее в число и вычисляет квадрат этого числа: Если пользователь передаст в метод не число, а строку, которая содежит нецифровые символы, то программа выпадет в ошибку. С одной стороны, здесь как раз та ситуация, когда можно применить блок try..catch , чтобы обработать возможную ошибку. Однако гораздо оптимальнее было бы проверить допустимость преобразования: Метод int.TryParse() возвращает true , если преобразование можно осуществить, и false — если нельзя. При допустимости преобразования переменная x будет содержать введенное число. Так, не используя try. catch можно обработать возможную исключительную ситуацию. С точки зрения производительности использование блоков try..catch более накладно, чем применение условных конструкций. Поэтому по возможности вместо try..catch лучше использовать условные конструкции на проверку исключительных ситуаций.double z
Error! The End.
Division by zero! The End.
throw std::string;
void print(int argument) noexcept;
Создание объекта исключения
Обработка и генерация разных типов исключений
#include double divide(int a, int b) < if(!b) // если b == 0 < throw 0; >if(b > a) < throw "The second number is greater than the first one"; >return a / b; > void test(int a, int b) < try < double result
throw 0;
throw "The second number is greater than the first one";
test(100, 20); // 5
test(100, 0); // Error code: 0
test(100, 1000); // The second number is greater than the first one
5 Error code: 0 The second number is greater than the first one
void test(int a, int b) < try < double result
throw 0;
try-catch и деструкторы
#include class Person < public: Person(std::string name) :name< name > < std::cout ~Person() < std::cout void print() < throw "Print Error"; >private: std::string name; >; int main() < try < Person tom< "Tom" >; tom.print(); // Здесь генерируется ошибка > catch (const char* error) < std::cerr >
Person Tom created Person Tom deleted Print Error
Как пропустить ошибку c
info@planetaexcel.ru
ООО «Планета Эксел»
ИНН 7735603520
ОГРН 1147746834949ИП Павлов Николай Владимирович
ИНН 633015842586
ОГРНИП 310633031600071 Обработка исключений
int x = 5; int y = x / 0; Console.WriteLine($"Результат: "); Console.WriteLine("Конец программы");

try < int x = 5; int y = x / 0; Console.WriteLine($"Результат: "); > catch < Console.WriteLine("Возникло исключение!"); >finally < Console.WriteLine("Блок finally"); >Console.WriteLine("Конец программы");
int y = x / 0;
Возникло исключение! Блок finally Конец программы
Обработка исключений и условные конструкции
Square("12"); // Квадрат числа 12: 144 Square("ab"); // !Исключение void Square(string data) < int x = int.Parse(data); Console.WriteLine($"Квадрат числа : "); >
Square("12"); // Квадрат числа 12: 144 Square("ab"); // Некорректный ввод void Square(string data) < if (int.TryParse(data, out var x)) < Console.WriteLine($"Квадрат числа : "); > else < Console.WriteLine("Некорректный ввод"); >>