C try catch типы ошибок

One of the advantages of C++ over C is Exception Handling. Exceptions are runtime anomalies or abnormal conditions that a program encounters during its execution. There are two types of exceptions: a)Synchronous, b)Asynchronous (i.e., exceptions which are beyond the program’s control, such as disc failure, keyboard interrupts etc.). C++ provides the following specialized keywords for this purpose:
try: Represents a block of code that can throw an exception.
catch: Represents a block of code that is executed when a particular exception is thrown.
throw: Used to throw an exception. Also used to list the exceptions that a function throws but doesn’t handle itself.

Why Exception Handling? 
The following are the main advantages of exception handling over traditional error handling:

1) Separation of Error Handling code from Normal Code: In traditional error handling codes, there are always if-else conditions to handle errors. These conditions and the code to handle errors get mixed up with the normal flow. This makes the code less readable and maintainable. With try/catch blocks, the code for error handling becomes separate from the normal flow.

2) Functions/Methods can handle only the exceptions they choose: A function can throw many exceptions, but may choose to handle some of them. The other exceptions, which are thrown but not caught, can be handled by the caller. If the caller chooses not to catch them, then the exceptions are handled by the caller of the caller. 
In C++, a function can specify the exceptions that it throws using the throw keyword. The caller of this function must handle the exception in some way (either by specifying it again or catching it).

3) Grouping of Error Types: In C++, both basic types and objects can be thrown as exceptions. We can create a hierarchy of exception objects, group exceptions in namespaces or classes and categorize them according to their types.
 

C++ Exceptions:

When executing C++ code, different errors can occur: coding errors made by the programmer, errors due to wrong input, or other unforeseeable things.

When an error occurs, C++ will normally stop and generate an error message. The technical term for this is: C++ will throw an exception (error).

C++ try and catch:

Exception handling in C++ consists of three keywords: try, throw and catch:

The try statement allows you to define a block of code to be tested for errors while it is being executed.

The throw keyword throws an exception when a problem is detected, which lets us create a custom error.

The catch statement allows you to define a block of code to be executed if an error occurs in the try block.

The try and catch keywords come in pairs:

We use the try block to test some code: If the value of a variable “age” is less than 18, we will throw an exception, and handle it in our catch block.

In the catch block, we catch the error if it occurs and do something about it. The catch statement takes a single parameter. So, if the value of age is 15 and that’s why we are throwing an exception of type int in the try block (age), we can pass “int myNum” as the parameter to the catch statement, where the variable “myNum” is used to output the value of age.

If no error occurs (e.g. if age is 20 instead of 15, meaning it will be greater than 18), the catch block is skipped.

Exception Handling in C++

1) The following is a simple example to show exception handling in C++. The output of the program explains the flow of execution of try/catch blocks. 

CPP

#include <iostream>

using namespace std;

int main()

{

   int x = -1;

   cout << "Before try \n";

   try {

      cout << "Inside try \n";

      if (x < 0)

      {

         throw x;

         cout << "After throw (Never executed) \n";

      }

   }

   catch (int x ) {

      cout << "Exception Caught \n";

   }

   cout << "After catch (Will be executed) \n";

   return 0;

}

Output: 

Before try
Inside try
Exception Caught
After catch (Will be executed)

2) There is a special catch block called the ‘catch all’ block, written as catch(…), that can be used to catch all types of exceptions. For example, in the following program, an int is thrown as an exception, but there is no catch block for int, so the catch(…) block will be executed. 

CPP

#include <iostream>

using namespace std;

int main()

{

    try  {

       throw 10;

    }

    catch (char *excp)  {

        cout << "Caught " << excp;

    }

    catch (...)  {

        cout << "Default Exception\n";

    }

    return 0;

}

Output: 

Default Exception

3) Implicit type conversion doesn’t happen for primitive types. For example, in the following program, ‘a’ is not implicitly converted to int. 

CPP

#include <iostream>

using namespace std;

int main()

{

    try  {

       throw 'a';

    }

    catch (int x)  {

        cout << "Caught " << x;

    }

    catch (...)  {

        cout << "Default Exception\n";

    }

    return 0;

}

Output: 

Default Exception

4) If an exception is thrown and not caught anywhere, the program terminates abnormally. For example, in the following program, a char is thrown, but there is no catch block to catch the char.  

CPP

#include <iostream>

using namespace std;

int main()

{

    try  {

       throw 'a';

    }

    catch (int x)  {

        cout << "Caught ";

    }

    return 0;

}

Output: 

terminate called after throwing an instance of 'char'

This application has requested the Runtime to terminate it in an 
unusual way. Please contact the application's support team for 
more information.

We can change this abnormal termination behavior by writing our own unexpected function.
5) A derived class exception should be caught before a base class exception. See this for more details.
6) Like Java, the C++ library has a standard exception class which is the base class for all standard exceptions. All objects thrown by the components of the standard library are derived from this class. Therefore, all standard exceptions can be caught by catching this type
7) Unlike Java, in C++, all exceptions are unchecked, i.e., the compiler doesn’t check whether an exception is caught or not (See this for details). So, it is not necessary to specify all uncaught exceptions in a function declaration. Although it’s a recommended practice to do so. For example, the following program compiles fine, but ideally the signature of fun() should list the unchecked exceptions. 

CPP

#include <iostream>

using namespace std;

void fun(int *ptr, int x)

{

    if (ptr == NULL)

        throw ptr;

    if (x == 0)

        throw x;

}

int main()

{

    try {

       fun(NULL, 0);

    }

    catch(...) {

        cout << "Caught exception from fun()";

    }

    return 0;

}

Output: 

Caught exception from fun()

A better way to write the above code: 

CPP

#include <iostream>

using namespace std;

void fun(int *ptr, int x) throw (int *, int)

{

    if (ptr == NULL)

        throw ptr;

    if (x == 0)

        throw x;

}

int main()

{

    try {

       fun(NULL, 0);

    }

    catch(...) {

        cout << "Caught exception from fun()";

    }

    return 0;

}

Note : The use of Dynamic Exception Specification has been deprecated since C++11. One of the reasons for it may be that it can randomly abort your program. This can happen when you throw an exception of another type which is not mentioned in the dynamic exception specification. Your program will abort itself because in that scenario, it calls (indirectly) terminate(), which by default calls abort().

Output: 

Caught exception from fun()

8) In C++, try/catch blocks can be nested. Also, an exception can be re-thrown using “throw; “. 

CPP

#include <iostream>

using namespace std;

int main()

{

    try {

        try {

            throw 20;

        }

        catch (int n) {

            cout << "Handle Partially ";

            throw;

        }

    }

    catch (int n) {

        cout << "Handle remaining ";

    }

    return 0;

}

Output: 

Handle Partially Handle remaining

A function can also re-throw a function using the same “throw; ” syntax. A function can handle a part and ask the caller to handle the remaining.
9) When an exception is thrown, all objects created inside the enclosing try block are destroyed before the control is transferred to the catch block.

CPP

#include <iostream>

using namespace std;

class Test {

public:

    Test() { cout << "Constructor of Test " << endl; }

    ~Test() { cout << "Destructor of Test " << endl; }

};

int main()

{

    try {

        Test t1;

        throw 10;

    }

    catch (int i) {

        cout << "Caught " << i << endl;

    }

}

Output: 

Constructor of Test
Destructor of Test
Caught 10

10) You may like to try Quiz on Exception Handling in C++.
Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.
 

Содержание

  • Исключения (Exceptions) и инструкция try
  • Оговорка catch
  • Блок finally
  • Инструкция using
  • Выбрасывание исключений
  • Основные свойства System.Exception
  • Основные типы исключений
  • Директивы препроцессора
    • Pragma Warning
    • Атрибут Conditional
  • Классы Debug и Trace
    • TraceListener
    • Fail и Assert

Исключения, их обработка, и некоторые другие моменты, связанные с ошибками в приложении на C#.

Исключения (Exceptions) и инструкция try

Инструкция try отмечает блок кода как объект для обработки ошибок или очистки. После блока try обязательно должен идти либо блок catch, либо блок finally, либо они оба. Блок catch выполняется, когда внутри блока try возникает ошибка. Блок finally выполняется после того, как прекращает выполнять блок try (или, если присутствует, блок catch), независимо от того, выполнился ли он до конца или был прерван ошибкой, что позволяет выполнить так называемый код очистки.

Блок catch имеет доступ к объекту исключения (Exception), который содержит информацию об ошибке. Блок catch позволяет обработать исключительную ситуацию и как-либо скорректировать ошибку или выбросить новое исключение. Повторное выбрасывание исключения в блоке catch обычно применяется с целью логирования ошибок или чтобы выбросить новое, более специфическое исключение.

Блок finally добавляет в программу прогнозируемость, позволяя выполнить определенный код при любых обстоятельствах. Это может быть полезно для выполнения операций очистки, например, закрытия сетевого подключения и т.д.

В целом конструкция try выглядит следующим образом:

try

{

  ... // в пределах этого блока может быть выброшено исключение

}

catch (ExceptionA ex)

{

  ... // обработчик исключений типа ExceptionA

}

catch (ExceptionB ex)

{

  ... // обработчик исключений типа ExceptionB

}

finally

{

  ... // код очистки

}

Например, следующий код выбросит ошибку DivideByZeroException (поскольку делить на ноль нельзя) и наша программа завершить досрочно:

int x = 3, y = 0;

Console.WriteLine (x / y);

Чтобы этого избежать можно использовать конструкцию try:

try

{

  int x = 3, y = 0;

  Console.WriteLine (x / y);

}

catch (DivideByZeroException ex)

{

  Console.Write («y cannot be zero. «);

}

// выполнение программы продолжится отсюда

Обработка исключений довольно ресурсоёмкая операция, поэтому на практике для таких случаев как в примере ее лучше не использовать (лучше непосредственно перед делением проверить делить на равенство нулю).

Когда выбрасывается исключение, CLR проверяет выброшено ли оно непосредственно внутри блока try, который может обработать данное исключение. Если да, выполнение переходит в соответствующий блок catch. Если блок catch успешно завершается, выполнение переходит к следующей после блока try инструкции (если имеется блок finally, то сначала выполняется он). Если же исключение выброшено не внутри блока try или конструкция try не содержит соответствующего блока catch, выполнение переходит в точку вызова метода (при этом сначала выполняется блок finally), и проверка повторяется снова.

Если не одна функция в стэке вызовов не способна обработать исключение, ошибка выводиться пользователю и программа завершается досрочно.

Оговорка catch

В оговорке catch указывается какой тип исключения она должна перехватывать. Это может быть либо System.Exception, либо его производный класс. Перехватывая непосредственно System.Exception, мы перехватим все возможные ошибки.  Это может быть полезно в нескольких случаях:

  • программа потенциально должна и может продолжить работать несмотря на ошибки любых типов
  • исключение будет выброшено повторно в блоке catch, например, после логирования ошибок
  • блок catch является последним в очереди, способным предотвратить аварийное завершение программы

Однако обычно перехватываются исключения более специфического типа, чтобы избежать ситуации, когда обработчику ошибки придется иметь дело с исключением, для которого он не предназначен (например, OutOfMemoryException).

Можно обработать несколько типов исключений с помощью нескольких оговорок catch:

try

{

  DoSomething();

}

catch (IndexOutOfRangeException ex) { ... }

catch (FormatException ex) { ... }

catch (OverflowException ex) { ... }

Каждая оговорка способна обработать только то исключение, которое точно совпадает с ее типом. Для одного выброшенного исключения может быть выполнена только одна оговорка catch. Обрабатываются блоки catch в том порядке, в котором они идут в коде. В этой связи более специфические исключения должны перехватываться раньше чем более общие.

Исключение может быть перехвачено и без указания переменной, если не нужен доступ к ее членам:

catch (StackOverflowException) // без переменной

{ ... }

Более того, в оговорке catch можно опустить и переменную и тип исключения — такая оговрка будет перехватывать все исключения:

Блок finally

Блок finally выполняется всегда, независимо от того выброшено исключение или нет. Блок finally обычно содержит код очистки.

Блок finally выполняется в следующих случаях:

  • после завершения блока catch
  • если выполнение блока try прервано jump-инструкциями: return, goto и т.д.
  • после выполнения блока try полностью, если исключений так и не было выброшено

Блок finally делает программу более прогнозируемой. Например, в следующем примере открываемый файл в итоге всегда будет закрыт, независимо от того, завершиться ли блок try без ошибок, или будет прерван выброшенным исключением, или сработает инструкция return если файл окажется пустым:

static void ReadFile()

{

  StreamReader reader = null;

  try

  {

      reader = File.OpenText («file.txt»);

      if (reader.EndOfStream) return;

      Console.WriteLine (reader.ReadToEnd());

  }

  finally

  {

      if (reader != null) reader.Dispose();

  }

}

В пример для закрытия файла вызывается метод Dispose. Использование этого метода внутри блока finally является стандартной практикой. C# даже позволяет заменить всю конструкцию инструкцией using.

Инструкция using

Многие классы инкапсулируют неуправляемые ресурсы, такие как дескриптор файла, соединение с базой данных и т.д. Эти классы реализуют интерфейс System.IDisposable, который содержит единственный метод без параметров Dispose, освобождающий соответствующие машинные ресурсы. Инструкция using предусматривает удобный синтаксис вызова метода Dispose для объектов реализующих IDisposable внутри блока finally:

using (StreamReader reader = File.OpenText («file.txt»))

{

  ...

}

Что эквивалентно следующей конструкции:

StreamReader reader = File.OpenText («file.txt»);

try

{

  ...

}

finally

{

  if (reader != null) ((IDisposable)reader).Dispose();

}

Выбрасывание исключений

Исключение может быть выброшено автоматически во время выполнения программы либо явно в коде программы с помощью ключевого слова throw:

static void Display (string name)

{

  if (name == null)

  throw new ArgumentNullException («name»);

  Console.WriteLine (name);

}

Также исключение может быть выброшено повторно внутри блока catch:

try { ... }

catch (Exception ex)

{

  // логирование ошибки

  ...

  throw; // повторное выбрасывание того же самого исключения

}

Такой подход позволяет заносить ошибки в лог без их дальнейшего поглощения. Также это позволяет уклониться от обработки неожиданных исключений.

Если throw заменить на throw ex, то пример по прежнему будет работать, но свойство исключения StackTrace не будет отражать исходную ошибку.

Другой распространенный сценарий использования повторного выбрасывания исключения — повторное выбрасывание более специфического и конкретного типа исключения, чем было перехвачено ранее:

try

{

  ... // парсинг даты рождения из xml-данных

}

catch (FormatException ex)

{

  throw new XmlException («Неправильная дата рождения», ex);

}

В таких случаях необходимо передать исходное исключение в качестве первого параметра конструктора нового исключения, ссылка на объект исходного исключения позже будет доступна через свойство InnerException внутреннего исключения.

Основные свойства System.Exception

К наиболее важным свойствам класса System.Exception можно отнести:

  • StackTrace — строка, представляющая все методы, которые были вызваны, начиная с того, в котором было выброшено исключение, и заканчивая тем, в котором содержится блок catch, перехвативший исключение;
  • Message — строка с описанием ошибки;
  • InnerException — содержит ссылку на объект Exeption, который вызвал текущее исключение (например, при повторном выбрасывании исключения).

Основные типы исключений

Следующие типы исключений являются наиболее распространенными в среде CLR и .NET Framework. Их можно выбрасывать непосредственно или использовать как базовые классы для пользовательских типов исключений.

  • System.ArgumentException — выбрасывается при вызове функции с неправильным аргументом.
  • System.ArgumentNullException — производный от ArgumentException класс, выбрасывается если один из аргументов функции неожиданно равен null.
  • System.ArgumentOutOfRangeException — производный от ArgumentException класс, выбрасывается когда аргумент функции имеет слишком большое или слишком маленькое значение для данного типа (обычно касается числовых типов). Например, такое исключение будет выброшено если попытаться передать отрицательное число в функцию, которая ожидает только положительные числа.
  • System.InvalidOperationException — выбрасывается когда состояние объекта является неподходящим для нормального выполнения метода, например, при попытке прочесть не открытый файл.
  • System.NotSupportedException — выбрасывается, когда запрошенный функционал не поддерживается, например, если попытаться вызвать метод Add для коллекции доступной только для чтения (свойство коллекции IsReadOnly возвращает true).
  • System.NotImplementedException — выбрасывается, когда запрошенный функционал еще не реализован.
  • System.ObjectDisposedException — выбрасывается при попытке вызвать метод объекта, который уже был уничтожен (disposed).

Директивы препроцессора

Директивы препроцессора снабжают компилятор дополнительной информацией об областях кода. Самые распространенные директивы препроцессора — условные директивы, позволяющие включить или исключить области кода из компиляции.

#define DEBUG

class MyClass

{

  int x;

  void Foo()

  {

      # if DEBUG

      Console.WriteLine («Testing: x = {0}», x);

      # endif

  }

}

В этом классе инструкции в методе Foo скомпилируются если определен символ DEBUG, а если его удалить — инструкции не скомпилируются. Символы препроцессора могут быть определены в исходном коде (как в примере), а могут быть переданы компилятору в командной строке с помощью параметра /define:symbol.

С директивами #if и #elif можно использовать операторы ||, && и ! с несколькими символами:

Директивы #error и #warning предотвращают некорректное использование условных директив, заставляя компилятор генерировать предупреждение или ошибку при передаче неверного набора символов.

Директивы препроцессора схожи с условными конструкциями и статическими переменными, однако дают возможности, недоступные для последних:

  • условное включение атрибута
  • изменение типа, объявляемого для переменной
  • переключение между разными пространствами имен или псевдонимами типа в директиве using:

    using TestType =

      #if V2

          MyCompany.Widgets.GadgetV2;

      #else

          MyCompany.Widgets.Gadget;

      #endif

  • создавать новые версии кода и быстро переключаться между ними при компиляции
  • создавать библиотеки, компилируемые для разных версий .NET Framework

Полный список директив препроцессора:

  • #define symbol — определяет символ
  • #undef symbol — удаляет символ
  • #if symbol [оператор symbol2]... — условная компиляция; допустимые операторы ==, !=, && и ||
  • #else — выполняет код после #endif
  • #elif symbol [оператор symbol2] — объединяет #else и #if
  • #endif — конец условных директив
  • #warning text — текст предупреждения, которое появится в выдаче компилятора
  • #error text — текст ошибки, которая появится в выдаче компилятора
  • #line [число["файл"] | hidden]число указывает номер строки в исходном коде; файл — имя файла, которое появится в выдаче компилятора; hidden — дает указание дебагеру пропустить код от этой точки до следующей директивы #line
  • #region name — отмечает начало области
  • #endregion — отмечает конец области
  • #pragma warning

Pragma Warning

Компилятор генерирует предупреждения, когда что-то в коде ему кажется неуместным (но корректным). В отличии от ошибок предупреждения не препятствуют компиляции программы. Предупреждения компилятора могут быть очень полезны при поиске багов в программе. Однако часто предупреждения оказываются ложными, поэтому целесообразно иметь возможность получать предупреждения только о действительных багах. С этой целью компилятор дает возможность выборочно подавить предупреждения с помощью директивы #pragma warning.

public class Foo

{

  static void Main() { }

  #pragma warning disable 414

  static string Message = «Hello»;

  #pragma warning restore 414

}

В примере мы указываем компилятору не выдавать предупреждения о том, что поле Message не используется.

Если не указывать номер директива #pragma warning отменит или восстановит вывод всех предупреждений.

Если скомпилировать программу с параметром /warnaserror, то все не отмененные директивой #pragma warning предупреждения будут расцениваться компилятором как ошибки.

Атрибут Conditional

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

[Conditional («LOGGINGMODE»)]

static void LogStatus (string msg)

{

  ...

}

Это равносильно тому, что каждый вызов метода будет окружен условными директивами:

#if LOGGINGMODE

LogStatus («Message Headers: « + GetMsgHeaders());

#endif

Классы Debug и Trace

Статические классы Debug и Trace предлагают базовые возможности логирования. Оба класса схожи, отличие заключается в их назанчении. Класс Debug предназначен для отладочных сборок, класс Trace — для отладочных и финальных. В связи с этим все методы класса Debug определены с атрибутом [Conditional("DEBUG")], а методы класса Trace — с атрибутом [Conditional("TRACE")]. Это значит, что все обращения к Debug и Trace будут подавляться компилятором, пока не определен символ DEBUG или TRACE.

Класс Debug и Trace определяют методы Write, WriteLine и WriteIf. По умолчанию они отправляют сообщения в окно вывода отладчика:

Debug.Write («Data»);

Debug.WriteLine (23 * 34);

int x = 5, y = 3;

Debug.WriteIf (x > y, «x is greater than y»);

Класс Trace также содержит методы TraceInformation, TraceWarning и TraceError. Их действия зависят от зарегистрированных прослушивателей.

TraceListener

Классы Debug и Trace имеют свойство Listeners, которое представляет собой статическую коллекцию экземпляров TraceListener. Они отвечают за обработку данных, возвращаемых методами Write, Fail и Trace.

По умолчанию коллекция Listeners обоих классов включает единственный прослушиватель — DefaultTraceListener — стандартный прослушиватель, имеющий две ключевые возможности:

  • при подключении к отладчику (например, Visual Studio) сообщения записываются в окно вывода отладчика, во всех остальных случаях сообщения игнорируются
  • при вызове метода Fail отображается диалоговое окно, запрашивающее у пользователя дальнейшие действия: продолжить, прервать или повторить отладку (независимо от того, подключен ли отладчик)

Это поведение можно изменить или дополнить, удалив (на обязательно) стандартный прослушиватель и/или добавив один или более собственных прослушивателей.

Прослушиваетли трассировки можно написать с нуля (создав производный класс от TraceListener) или воспользоваться готовыми классами:

  • TextWriterTraceListener записывает в Stream или TextWriter или добавляет в файл; имеет четыре подкласса: ConsoleTraceListener, DelimitedListTraceListener, XmlWriterTraceListener и EventSchemaTraceListener
  • EventLogTraceListener записывает в журнал событий Windows
  • EventProviderTraceListener записывает в систему трассировки событий Windows (Event Tracing for Windows — ETW)
  • WebPageTraceListener выводит на веб-страницу ASP.NET

Ни один из этих прослушивателе не отображает диалоговое окно при вызове Fail, это делает только DefaultTraceListener.

// Удалить стандартный прослушиватель, очистив коллекцию прослушивателей:

Trace.Listeners.Clear();

// Добавить средство записи в файл trace.txt:

Trace.Listeners.Add (new TextWriterTraceListener («trace.txt»));

// Добавит средство записи в консоль:

System.IO.TextWriter tw = Console.Out;

Trace.Listeners.Add (new TextWriterTraceListener (tw));

// Добавить средство записи в журнал событий Windows:

if (!EventLog.SourceExists («DemoApp»))

  EventLog.CreateEventSource («DemoApp», «Application»);

Trace.Listeners.Add (new EventLogTraceListener («DemoApp»));

В случае журнала событий Windows сообщения, отправляемые с помощью Write, Fail или Assert, записываются как сведения, а сообщения методов TraceWarning и TraceError записываются как предупреждения или ошибки.

Каждый экземпляр TraceListener имеет свойство Filter и TraceFilter, с помощью которых можно управлять, будет ли сообщение записано в этот прослушиватель. Для этого необходимо создать экземпляр классов EventTypeFilter или SourceFilter (производных от TraceFilter) или создать свой класс, наследующий от TraceFilter и переопределить в нем метод ShouldTrace.

В TraceListener также определены свойства IndentLevel и IndentSize для управления отступами и свойство TraceOutputOptions для записи дополнительных данных:

TextWriterTraceListener tl = new TextWriterTraceListener (Console.Out);

tl.TraceOutputOptions = TraceOptions.DateTime | TraceOptions.Callstack;

// Это применяется при использовании метода Trace:

Trace.TraceWarning («Orange alert»);

DiagTest.vshost.exe Warning: 0 : Orange alert

DateTime=20070308T05:57:13.6250000Z

Callstack= at System.Environment.GetStackTrace(Exception e, Boolean

needFileInfo)

at System.Environment.get_StackTrace() at ...

Прослушиватели, которые записывают данные в поток, кэшируются. По этой причине данные не появляются в потоке немедленно, а также поток перед завершением приложения должен быть закрыт, или хотя бы сброшен, чтоб не потерять данные в кэше. Для этой цели классы Trace и Debug содержат статические методы Close и Flush, которые вызывают Close и Flush во всех прослушивателях (а они в свою очередь закрывают или сбрасывают все потоки). Метод Close вызывает метод Flush, закрывает файловые дескрипторы и предотвращает дальнейшую запись.

Классы Trace и Debug также определяют свойство AutoFlush, которое если равно true вызывает Flush после каждого сообщения.

Fail и Assert

Классы Debug и Trace содержат методы Fail и Assert.

Метод Fail отправляет сообщения каждому TraceListener:

Debug.Fail («File data.txt does not exist!»);

Метод Assert вызывает Fail если аргумент типа bool равен false. Это называется созданием утверждения и указывает на ошибку, если оно нарушено. Можно также создать необязательное сообщение об ошибке:

Debug.Assert (File.Exists («data.txt»), «File data.txt does not exist!»);

var result = ...

Debug.Assert (result != null);

Методы Write, Fail и Assert также могут принимать категорию в виде строки ,которая может быть использована при обработке вывода.

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

Конструкция try..catch..finally

Последнее обновление: 30.12.2021

Иногда при выполнении программы возникают ошибки, которые трудно предусмотреть или предвидеть, а иногда и вовсе невозможно. Например, при передачи файла по сети может неожиданно оборваться сетевое подключение.
такие ситуации называются исключениями. Язык C# предоставляет разработчикам возможности для обработки таких ситуаций. Для этого
в C# предназначена конструкция try…catch…finally.

try
{
	
}
catch
{
	
}
finally
{
	
}

При использовании блока try…catch..finally вначале выполняются все инструкции в блоке try. Если в
этом блоке не возникло исключений, то после его выполнения начинает выполняться блок finally. И затем конструкция try..catch..finally
завершает свою работу.

Если же в блоке try вдруг возникает исключение, то обычный порядок выполнения останавливается, и среда CLR
начинает искать блок catch, который может обработать данное исключение. Если нужный блок
catch найден, то он выполняется, и после его завершения выполняется блок finally.

Если нужный блок catch не найден, то при возникновении исключения программа аварийно завершает свое выполнение.

Рассмотрим следующий пример:

int x = 5;
int y = x / 0;
Console.WriteLine($"Результат: {y}");
Console.WriteLine("Конец программы");

В данном случае происходит деление числа на 0, что приведет к генерации исключения. И при запуске приложения в
режиме отладки мы увидим в Visual Studio окошко, которое информирует об исключении:

Исключения в C#

В этом окошке мы видим, что возникло исключение, которое представляет тип System.DivideByZeroException,
то есть попытка деления на ноль. С помощью пункта View Details можно посмотреть более детальную информацию об исключении.

И в этом случае единственное, что нам остается, это завершить выполнение программы.

Чтобы избежать подобного аварийного завершения программы, следует использовать для обработки исключений конструкцию
try…catch…finally. Так, перепишем пример следующим образом:

try
{
	int x = 5;
	int y = x / 0;
	Console.WriteLine($"Результат: {y}");
}
catch
{
	Console.WriteLine("Возникло исключение!");
}
finally
{
	Console.WriteLine("Блок finally");
}
Console.WriteLine("Конец программы");

В данном случае у нас опять же возникнет исключение в блоке try, так как мы пытаемся разделить на ноль.
И дойдя до строки

int y = x / 0;

выполнение программы остановится. CLR найдет блок catch и передаст управление этому блоку.

После блока catch будет выполняться блок finally.

Возникло исключение!
Блок finally
Конец программы

Таким образом, программа по-прежнему не будет выполнять деление на ноль и соответственно не будет выводить результат этого деления,
но теперь она не будет аварийно завершаться, а исключение будет обрабатываться в блоке catch.

Следует отметить, что в этой конструкции обязателен блок try. При наличии блока catch мы можем опустить блок finally:

try
{
	int x = 5;
	int y = x / 0;
	Console.WriteLine($"Результат: {y}");
}
catch
{
	Console.WriteLine("Возникло исключение!");
}

И, наоборот, при наличии блока finally мы можем опустить блок catch и не обрабатывать исключение:

try
{
	int x = 5;
	int y = x / 0;
	Console.WriteLine($"Результат: {y}");
}
finally
{
	Console.WriteLine("Блок finally");
}

Однако, хотя с точки зрения синтаксиса C# такая конструкция вполне корректна, тем не менее, поскольку CLR не сможет найти нужный блок
catch, то исключение не будет обработано, и программа аварийно завершится.

Обработка исключений и условные конструкции

Ряд исключительных ситуаций может быть предвиден разработчиком. Например, пусть в программе есть метод, который принимает строку, конвертирует ее в число
и вычисляет квадрат этого числа:

Square("12"); // Квадрат числа 12: 144
Square("ab"); // !Исключение

void Square(string data)
{
    int x = int.Parse(data);
    Console.WriteLine($"Квадрат числа {x}: {x * x}");
}

Если пользователь передаст в метод не число, а строку, которая содежит нецифровые символы, то программа выпадет в ошибку. С одной стороны,
здесь как раз та ситуация, когда можно применить блок
try..catch, чтобы обработать возможную ошибку. Однако гораздо оптимальнее было бы проверить допустимость преобразования:

Square("12"); // Квадрат числа 12: 144
Square("ab"); // Некорректный ввод

void Square(string data)
{
    if (int.TryParse(data, out var x))
    {
        Console.WriteLine($"Квадрат числа {x}: {x * x}");
    }
    else
    {
        Console.WriteLine("Некорректный ввод");
    }
}

Метод int.TryParse() возвращает true, если преобразование можно осуществить, и false — если нельзя. При допустимости преобразования переменная x
будет содержать введенное число. Так, не используя try...catch можно обработать возможную исключительную ситуацию.

С точки зрения производительности использование блоков try..catch более накладно, чем применение условных конструкций. Поэтому по возможности
вместо try..catch лучше использовать условные конструкции на проверку исключительных ситуаций.

Понятие исключительной ситуации. Блок try…catch. Оператор throw. Примеры использования


Содержание

  • 1. Типы ошибок, которые могут возникать в программах
  • 2. Понятие исключительной ситуации
  • 3. Понятие исключения
  • 4. Средства языка C++ для обработки исключительных ситуаций. Общая форма конструкции try…catch. Назначение
  • 5. Оператор throw. Назначение
  • 6. Примеры использования блока try…catch
  • 7. Пример использования блока try…catch в функции
  • 8. Пример программы, которая генерирует исключение в одной функции, а перехватывает его в другой функции
  • 9. Использование блока catch(…). Перехват всех возможных исключительных ситуаций. Пример
  • Связанные темы

Поиск на других ресурсах:

1. Типы ошибок, которые могут возникать в программах

В программах на C++ могут возникать ошибки. Различают три типа ошибок, которые могут возникать в программах:

  • синтаксические. Это ошибки в синтаксисе языка C++. Они могут встречаться в именах операторов, функций, разделителей и т.д. В этом случае компилятор определяет наличие синтаксической ошибки и выдает соответствующее сообщение. В результате исполняющий (*.exe) файл не создается и программа не выполняется;
  • логические. Это ошибки программиста, которые сложно обнаружить на этапе разработки программы. Эти ошибки обнаруживаются на этапе выполнения во время тестирования работы программы. Логические ошибки можно обнаружить только по результатам работы программы. Примером логических ошибок может быть неправильная работа с указателями в случаях выделения/освобождения памяти;
  • ошибки времени выполнения. Такие ошибки возникают во время работы программы. Ошибки времени выполнения могут быть логическими ошибками программиста, ошибками внешних событий (например, нехватка оперативной памяти), неверным вводом данных пользователем и т.п. В результате возникновения ошибки времени выполнения, программа приостанавливает свою работу. Поэтому, важно перехватить эту ошибку и правильно обработать ее для того, чтобы программа продолжила свою работу без остановки.

Данная тема освещает применение механизма перехвата ошибок времени выполнения.

 

2. Понятие исключительной ситуации

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

Примеры действий в программе, которые могут привести к возникновению исключительных ситуаций:

  • деление на нуль;
  • нехватка оперативной памяти при использовании оператора new для ее выделения (или другой функции);
  • доступ к элементу массива за его пределами (ошибочный индекс);
  • переполнение значения для некоторого типа;
  • взятие корня из отрицательного числа;
  • другие ситуации.

 

3. Понятие исключения

В языке C++ исключение – это специальный объект класса или значение базового типа, который описывает (определяет) конкретную исключительную ситуацию и соответствующим образом обрабатывается.

При написании программы система описания исключительных ситуаций выбирается программистом по собственному усмотрению. Можно создать свою квалификацию ошибок, которые могут возникать в программе. Например, программист может квалифицировать разные типы ошибок числовым (целочисленным) значением или разработать собственную иерархию классов описывающих исключительные ситуации. Кроме того, можно использовать возможности классов C++, которые являются производными от класса exception.

 

4. Средства языка C++ для обработки исключительных ситуаций. Общая форма конструкции try…catch. Назначение

Язык программирования C++ дает возможность перехватывать исключительные ситуации и соответствующим образом их обрабатывать.

Механизм перехвата исключений C++ позволяет генерировать исключения в том месте, в котором оно возникает – это очень удобно. Не нужно «выдумывать» собственные способы обработки исключений, которые возникают в функциях нижних уровней, для того чтобы передать их в функции высших уровней.

Для перехвата и обработки исключительных ситуаций в языке C++ введена конструкция try…catch, которая имеет следующую общую форму:

try {
  // тело блока try
  // ...
  // генерирование исключения оператором throw
}
catch(type1 argument1)
{
  // тело блока catch
}
catch(type2 argument2)
{
  // тело блока catch
}
...
catch(typeN argumentN)
{
  // тело блока catch
}

где

  • type1, type2, …, typeN – соответственно тип аргументов argument1, argument2, …, argumentN.

Код, который нужно проконтролировать, должен выполняться всередине блока try. Исключительные ситуации перехватываются оператором catch, который следует непосредственно за блоком try в котором они возникли.

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

Если в блоке try возникнет исключительная ситуация, которая не предусмотрена блоком catch, то вызывается стандартная функция terminate(), которая по умолчанию вызовет функцию abort(). Эта стандартная функция останавливает выполнение программы.

 

5. Оператор throw. Назначение

Чтобы в блоке try сгенерировать исключительную ситуацию, нужно использовать оператор throw. Оператор throw может быть вызван внутри блока try или внутри функции, которая вызывается из блока try.

Общая форма оператора throw следующая

throw исключение;

В результате выполнения оператора throw генерируется исключение некоторого типа. Это исключение должно быть обработано в блоке catch.

 

6. Примеры использования блока try…catch

Пример 1. Демонстрируется использование блока try…catch для обработки выражения:

В данном выражении в трех случаях может возникнуть исключительная ситуация:

  • корень из отрицательного числа a, если a<0;
  • корень из отрицательного числа b, если b<0;
  • деление на 0, если b=0.

Поэтому, в блоке try…catch нужно обработать эти три случая.

Текст программы типа Console Application следующий:

#include <iostream>
using namespace std;

void main()
{
  // обработка выражения sqrt(a)/sqrt(b)
  double a, b;
  cout << "a = ";
  cin >> a;

  cout << "b = ";
  cin >> b;

  double c;

  try { // начало блока try
    if (b == 0)
      throw 1;
    if (b < 0)
      throw 2;
    if (a < 0)
      throw 2;

    c = sqrt(a) / sqrt(b);
    cout << "c = " << c << endl;
  }
  catch (int e) // перехват ошибки
  {
    if (e == 1)
      cout << "Division by 0." << endl;
    if (e == 2)
      cout << "Negative root." << endl;
  }
}

Результат работы программы

a = 5
b = 0
Division by 0.

После применения блока try..catch работа программы не прекращается.

Пример 2. Другой вариант обработки выражения из примера 1. Здесь блок try…catch содержит два оператора catch.

#include <iostream>
using namespace std;

void main()
{
  // обработка выражения sqrt(a)/sqrt(b) - вариант 2
  double a, b;
  cout << "a = ";
  cin >> a;

  cout << "b = ";
  cin >> b;

  double c;
  string s;

  try { // начало блока try
    if (b == 0)
      throw "Division by 0.";
    if (b < 0)
      throw "Negative root.";
    if (a < 0)
      throw "Negative root.";

    // если исключительных ситуаций нет, то продолжить вычисления
    c = sqrt(a) / sqrt(b);
    cout << "c = " << c << endl;
  }
  catch (int e) // перехват ошибки типа int
  {
    if (e == 1)
      cout << "Division by 0." << endl;
    if (e == 2)
      cout << "Negative root." << endl;
  }
  catch (const char* e) // перехват ошибки типа const char*
  {
    cout << e << endl;
  }
}

 



7. Пример использования блока try…catch в функции

Условие задачи. Написать функцию вычисления значения по заданной строке символов, которая есть записью этого числа в десятичной системе исчисления. Предусмотреть случай выхода за границы диапазона, определяемого типом int. Необходимо использовать механизм исключений.

Текст программы для приложения типа Console Application следующий

#include <iostream>
using namespace std; 

// Функция вычисления значения по заданной строке символов
int StrToInt(const char* str)
{
  char s[20];
  int t, i;
  long res = 0; // результат возврата из функции
  int len = strlen(str);

  try
  {
    t = 1;
    if (str[0] == '-') 
      t = -1; // проверка, первый ли символ '-'

    // цикл конвертирования строки в число типа int
    i = len - 1;
    while (i >= 0)
    {
      if (str[i] == '-')
      {
        if (i == 0) break; // если перший символ '-', то все в порядке
        else throw "Bad position of minus.";
      }

      // если в строке недопустимые символы, то сгенерировать исключение
      if (str[i] < '0') throw "Bad symbols";
      if (str[i] > '9') throw "Bad symbols";

      res = res + (str[i] - '0')*t;

      t *= 10;
      i--;
    }

    // если результат выходит за пределы диапазона для 32-разрядных
    // целочисленных значений, то сгенерировать соответствующее исключения
    if (res > INT32_MAX)
      throw "Out of range.";
    if (res < INT32_MIN)
      throw "Out of range.";
    return res;
  }
  catch (const char* e)
  {
    cout << e << endl;
    return 0;
  }
}

void main()
{
  int d;
  d = StrToInt("125");
  cout << "d = " << d;
}

Вышеприведенная программа может быть переписана так, что блок try…catch размещается в функции main(), как показано ниже

#include <iostream>
using namespace std; 

// Функция вычисления значения по заданной строке символов
int StrToInt2(const char* str)
{
  char s[20];
  int t, i;
  long res = 0; // результат работы функции
  int len = strlen(str);

  t = 1;
  if (str[0] == '-') t = -1;

  i = len - 1;
  while (i >= 0)
  {
    if (str[i] == '-')
    {
      if (i == 0) break; // если первый символ '-', то все в порядке
      else throw "Bad position of minus."; // иначе, сгенерировать исключение
    }

    if (str[i] < '0') throw "Bad symbols";
    if (str[i] > '9') throw "Bad symbols";

    res = res + (str[i] - '0')*t;
    t *= 10;
    i--;
  }

  // если результат выходит за пределы диапазона для 32-разрядных
  // целочисленных значений, то сгенерировать соответствующее исключения
  if (res > INT32_MAX)
    throw "Out of range.";
  if (res < INT32_MIN)
    throw "Out of range.";
  return res;
}

void main()
{
  int d;

  // блок try...catch размещается в функции main() высшего уровня,
  // а исключение генерируется в функции StrToInt2() нижнего уровня
  try {
    d = StrToInt2("19125");
    cout << "d = " << d;
  }
  catch (const char* e)
  {
    cout << e << endl;
  }
}

Как видно из вышеприведенноо кода, генерировать исключения оператором throw можно в другой функции, вызов которой включен в блок try. Значит, функция в своем теле может генерировать исключение.

Результат выполнения программы

d = 19125

Если вызов функции StrToInt2() перенести за пределы оператора try

void main()
{
  int d;

  try {
    //d = StrToInt2("19125");
    //cout << "d = " << d;
  }
  catch (const char* e)
  {
    cout << e << endl;
  }

  // вызов функции за пределами оператора try
  d = StrToInt2("в19125");
}

то исключительные ситуации в функции StrToInt2() обрабатываться не будут. При возникновении исключительной ситуации в функции StrToInt2() компилятор сгенерирует собственную ошибку

Exception Unhandled

что означает, что исключение необработано.

 

8. Пример программы, которая генерирует исключение в одной функции, а перехватывает его в другой функции

В примере, в функции нижнего уровня GenerateException() генерируется исключение типа const char*. Функция проверяет допустимые границы входного параметра index.

В функции верхнего уровня ProcessException() происходит вызов функции GenerateException(). Этот вызов взят в блок try.

Текст программы следующий:

#include <iostream>
using namespace std;

// Пример Программы, которая генерирует исключение в одной функции, а перехватывает его в другой
// Функция генерирует исключение "Out of index",
// если значение index находится за пределами диапазона 0..9
void GenerateException(int index)
{
  if ((index < 0) || (index > 9))
    throw "Out of index";
}

// Функция, которая перехватывает исключение "Out of index"
void ProcessException()
{
  int index;
  cout << "index = ";
  cin >> index;

  // 1. Вызвать исключительную ситуацию без обработки,
  // компилятор выдаст сообщение "Exception unhandled"
  // GenerateException(-3);

  // 2. Вызвать исключительную ситуацию с обработкой блоком try...catch
  try {
    GenerateException(index); // вызов функции, которая генерирует исключение
    cout << "OK!" << endl; // если index в пределах 0..9, то OK!
  }
  catch (const char* e)
  {
    cout << "Error: " << e << endl;
  }
}

void main()
{
  ProcessException();
}

Результат выполнения программы

index = -5
Error: Out of index

 

9. Использование блока catch(…). Перехват всех возможных исключительных ситуаций. Пример

Бывают случаи, когда нужно перехватить все исключительные ситуации подряд. Для этого, в C++ используется блок catch(…), который имеет следующую общую форму

catch(...)
{
  // Обработка всех исключительных ситуаций
  // ...
}

Пример. В примере демонстрируется использование блока catch(…) для обработки ситуаций любого типа.

В программе реализованы:

  • функция DivNumbers(), которая возвращает результат деления двух чисел, введенных из клавиатуры. В функции генерируется исключение типа int, если значение делителя равно 0;
  • функция SqRoot(), возвращающая корень из отрицательного числа. В функции генерируется исключение типа const char*, если значение параметра number отрицательно;
  • функция ProcessException(). Эта функция демонстрирует работу функций DivNumbers() и SqRoot(). В функции используется инструкция try…catch().
#include <iostream> 
using namespace std; 

// Пример. Демонстрация использования блока catch
// Функция, которая делит 2 числа и возвращает результат
double DivNumbers(double a, double b)
{
  if (b == 0) throw 1;
    return a / b;
}

// Функция, возвращающая корень из отрицательного числа
double SqRoot(double number)
{
  if (number < 0) throw "Negative number";
    return sqrt(number);
}

// Демонстрация блока catch(...)
void ProcessException()
{
  double v;

  // цикл отображения и вызова нужной функции
  while (1)
  {
    cout << "Input a function to call (1-2, 3-exit): " << endl;
    cout << "1-DivNumbers. 2-SqRoot" << endl;
    cout << ">>";
    cin >> v;

    // Вызвать различные варианты функций
    try {
      if (v == 1) // функция DivNumbers
      {
        double a, b;
        cout << "DivNumbers(double a, double b)" << endl;

        // ввести a, b
        cout << "a = "; cin >> a;
        cout << "b = "; cin >> b;

        // Вызвать функцию DivNumbers()
        double c = DivNumbers(a, b);
        cout << "c = " << c << endl;
      }
      if (v == 2)
      {
        double x, num;
        cout << "SqRoot(double num)" << endl;
        cout << "num = "; cin >> num;

        // Вызвать функцию SqRoot()
        x = SqRoot(num);
        cout << "x = " << x << endl;
      }
      if (v == 3) break;
    }
    catch (const char* e)
    {
      cout << "Error. Text = " << e << endl;
    }
    catch (...) // все другие типы исключений
    {
      cout << "Error in block catch(...)." << endl;
    }
  }
}

void main()
{
  ProcessException();
}

Как видно из текста функции ProcessException() вызов функций DivNumbers() и SqRoot() взят в блок try…catch

// Вызвать разные варианты функций
try {
  ...
}
catch (const char* e)
{
  cout << "Error. Text = " << e << endl;
}
catch (...) // все другие типы исключений
{
  cout << "Error in block catch(...)." << endl;
}

В блоке try…catch обрабатываются

  • исключение типа const char*;
  • все другие типы исключений. В этом случае используется инструкция catch(…).

Результат работы программы

Input a function to call (1-2, 3-exit):
1-DivNumbers. 2-SqRoot
>>2
SqRoot(double num)
num = -4
Error. Text = Negative number
Input a function to call (1-2, 3-exit):
1-DivNumbers. 2-SqRoot
>>1
DivNumbers(double a, double b)
a = 3
b = 0
Error in block catch(...).
Input a function to call (1-2, 3-exit):
1-DivNumbers. 2-SqRoot
>>1
DivNumbers(double a, double b)
a = 2
b = 5
c = 0.4
Input a function to call (1-2, 3-exit):
1-DivNumbers. 2-SqRoot
>>3

 


Связанные темы

  • Пример создания иерархии классов для обработки исключительных ситуаций

 


Associates one or more exception handlers (catch-clauses) with a compound statement.

[edit] Syntax

try compound-statement handler-sequence

where handler-sequence is a sequence of one or more handlers, which have the following syntax:

catch ( attr (optional) type-specifier-seq declarator ) compound-statement (1)
catch ( attr (optional) type-specifier-seq abstract-declarator (optional) ) compound-statement (2)
catch ( ... ) compound-statement (3)
compound-statement brace-enclosed sequence of statements
attr (since C++11) any number of attributes, applies to the formal parameter
type-specifier-seq part of a formal parameter declaration, same as in a function parameter list
declarator part of a formal parameter declaration, same as in a function parameter list
abstract-declarator part of an unnamed formal parameter declaration, same as in function parameter list

1) Catch-clause that declares a named formal parameter

2) Catch-clause that declares an unnamed parameter

3) Catch-all handler, which is activated for any exception

try { /* */ } catch (...) { /* */ }

[edit] Explanation

See throw exceptions for more information about throw-expressions

A try-block is a statement, and as such, can appear anywhere a statement can appear (that is, as one of the statements in a compound statement, including the function body compound statement). See function-try-block for the try blocks around function bodies. The following description applies to both try-blocks and function-try-blocks.

The formal parameter of the catch clause (type-specifier-seq and declarator or type-specifier-seq and abstract-declarator) determines which types of exceptions cause this catch clause to be entered. It cannot be an incomplete type, abstract class type, rvalue reference type, (since C++11) or pointer to incomplete type (except that pointers to (possibly cv-qualified) void are allowed). If the type of the formal parameter is array type or function type, it is treated as the corresponding pointer type (similar to a function declaration).

When an exception is thrown by any statement in compound-statement, the exception object of type E is matched against the types of the formal parameters T of each catch-clause in handler-seq, in the order in which the catch clauses are listed. The exception is a match if any of the following is true:

  • E and T are the same type (ignoring top-level cv-qualifiers on T)
  • T is an lvalue-reference to (possibly cv-qualified) E
  • T is an unambiguous public base class of E
  • T is a reference to an unambiguous public base class of E
  • T is (possibly cv-qualified) U or const U& (since C++14), and U is a pointer or pointer to member type, and E is also a pointer or pointer to member type that is implicitly convertible to U by one or more of
  • a standard pointer conversion other than one to a private, protected, or ambiguous base class
  • a qualification conversion
  • T is a pointer or a pointer to member or a reference to a const pointer (since C++14), while E is std::nullptr_t.
(since C++11)
try
{
    f();
}
catch (const std::overflow_error& e)
{} // this executes if f() throws std::overflow_error (same type rule)
catch (const std::runtime_error& e)
{} // this executes if f() throws std::underflow_error (base class rule)
catch (const std::exception& e)
{} // this executes if f() throws std::logic_error (base class rule)
catch (...)
{} // this executes if f() throws std::string or int or any other unrelated type

The catch-all clause catch () matches exceptions of any type. If present, it has to be the last catch clause in the handler-seq. Catch-all block may be used to ensure that no uncaught exceptions can possibly escape from a function that offers nothrow exception guarantee.

If no matches are found after all catch-clauses were examined, the exception propagation continues to the containing try-block, as described in throw-expression. If there are no containing try-blocks left, std::terminate is executed (in this case, it is implementation-defined whether any stack unwinding occurs at all: throwing an uncaught exception is permitted to terminate the program without invoking any destructors).

When entering a catch clause, if its formal parameter is a base class of the exception type, it is copy-initialized from the base class subobject of the exception object. Otherwise, it is copy-initialized from the exception object (this copy is subject to copy elision).

try
{
    std::string("abc").substr(10); // throws std::length_error
}
// catch (std::exception e) // copy-initialization from the std::exception base
// {
//     std::cout << e.what(); // information from length_error is lost
// }
catch (const std::exception& e) // reference to the base of a polymorphic object
{
    std::cout << e.what(); // information from length_error printed
}

If the parameter of the catch-clause is a reference type, any changes made to it are reflected in the exception object, and can be observed by another handler if the exception is rethrown with throw;. If the parameter is not a reference, any changes made to it are local and its lifetime ends when the handler exits.

A goto or switch statement shall not be used to transfer control into a try block or into a handler.

Other than by throwing or rethrowing the exception, the catch-clause after a regular try block (not function-try-block) may be exited with a return, continue, break, goto, or by reaching the end of its compound-statement. In any case, this destroys the exception object (unless an instance of std::exception_ptr exists that refers to it).

[edit] Notes

The throw-expression throw NULL; is not guaranteed to be matched by a pointer catch clause, because the exception object type may be int, but throw nullptr; is assuredly matched by any pointer or pointer-to-member catch clause.

If a catch-clause for a derived class is placed after the catch-clause for a base class, the derived catch-clause will never be executed:

If goto is used to exit a try-block and if any of the destructors of block-scoped automatic variables that are executed by the goto throw exceptions, those exceptions are caught by the try blocks in which the variables are defined:

label:
    try
    {
        T1 t1;
        try
        {
            T2 t2;
            if (condition)
                goto label; // destroys t2, then destroys t1, then jumps to label
        }
        catch (...) {} // catches the exception from the destructor of t2
    }
    catch (...) {}     // catches the exception from the destructor of t1

[edit] Keywords

try,
catch,
throw

[edit] Example

The following example demonstrates several usage cases of the try-catch block

#include <iostream>
#include <vector>
 
int main()
{
    try
    {
        std::cout << "Throwing an integer exception...\n";
        throw 42;
    }
    catch (int i)
    {
        std::cout << " the integer exception was caught, with value: " << i << '\n';
    }
 
    try
    {
        std::cout << "Creating a vector of size 5... \n";
        std::vector<int> v(5);
        std::cout << "Accessing the 11th element of the vector...\n";
        std::cout << v.at(10); // vector::at() throws std::out_of_range
    }
    catch (const std::exception& e) // caught by reference to base
    {
        std::cout << " a standard exception was caught, with message '"
                  << e.what() << "'\n";
    }
}

Possible output:

Throwing an integer exception...
 the integer exception was caught, with value: 42
Creating a vector of size 5...
Accessing the 11th element of the vector...
 a standard exception was caught, with message 'out_of_range'

[edit] Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
CWG 98 C++98 a switch statement can transfer control
into a try block or into a handler
prohibited
CWG 210 C++98 the throw expression was matched against the catch clauses the exception object is matched
against the catch clauses
CWG 1166 C++98 the behavior was unspecified when a catch clause whose
exception type is a reference to an abstract class type is matched
abstract class types are not
allowed for catch clauses
CWG 1769 C++98 when the type of the exception declared in the catch-clause is a
base of the type of the exception object, a converting constructor
might be used for the initialization of the catch-clause parameter
the parameter is copy-initialized
from the corresponding base class
subobject of the exception object
CWG 2093 C++98 an exception object of pointer to object type could not match a
handler of pointer to object type through qualification conversion
allowed

Понравилась статья? Поделить с друзьями:
  • Bus gov ru ошибка при установлении защищенного соединения
  • C builder сообщение об ошибке
  • C0031 ошибка гранта
  • C 3b07 ошибка
  • C0031 ошибка abs