C++. The concept of an exceptional situation. Instruction try…catch. Operator throw. Examples of using

The concept of an exceptional situation. Instruction try…catch. Operator throw. Examples of using


Contents


1. Types of errors that can occur in programs

Errors can occur in C++ programs. There are three types of errors that can occur in programs:

  • syntactic. These are errors in the syntax of the C++ language. They can be found in the names of operators, functions, delimiters, etc. In this case, the compiler determines the presence of a syntax error and displays the corresponding message. As a result, the executing (*.exe) file is not created and the program is not executed;
  • logical. These are programmer errors that are difficult to detect at the stage of program development. These errors are detected at runtime during testing the program. Logical errors can be detected only by the results of the program. An example of logical errors can be incorrect operation with pointers in cases of memory allocation/deallocation;
  • runtime errors. Such errors occur while the program is running. Runtime errors can be logical errors of the programmer, errors of external events (for example, lack of RAM), incorrect user input, etc. As a result of a runtime error, the program pauses its work. Therefore, it is important to catch this error and correctly process it so that the program continues its work without stopping.

This topic highlights the use of the runtime errors trapping mechanism.

 

2. The concept of an exceptional situation

An exceptional situation is an event that caused the program to crash. As a result of an exceptional situation, the program cannot continue its execution correctly.

Examples of actions in the program that may lead to exceptional situations:

  • division by zero;
  • lack of RAM when using the new operator to allocate it (or another function);
  • access to an array element beyond its limits (erroneous index);
  • value overflow for some type;
  • taking the root from a negative number;
  • other situations.

 

3. Concept of exception

In C++, an exception is a special object of a class or a value of a base type that describes (defines) a specific exception and is handled accordingly.

When writing a program, the system for describing exceptional situations is chosen by the programmer at his discretion. You can create your own qualification of errors that may occur in the program. For example, a programmer can qualify different types of errors by a numerical (integer) value or develop their own hierarchy of classes describing exceptional situations. In addition, you can use the capabilities of C++ classes, which are derived from the class exception.

 

4. The tools of C++ language for handling exceptions. The general form of the try…catch construct

The C ++ programming language makes it possible to catch exceptions and handle them accordingly. The C++ exception catching mechanism allows you to throw exceptions in the place where it occurs – this is very convenient. There is no need to “invent” your own methods for handling exceptions that arise in functions of lower levels in order to pass them into functions of higher levels.

To catch and handle exceptions, the C++ language introduced the try…catch construct, which has the following general form:

try {
  // the body of try block
  // ...
  // throwing an exception with the throw statement
}
catch(type1 argument1)
{
  // the body of catch block
}
catch(type2 argument2)
{
  // the body of catch block
}
...
catch(typeN argumentN)
{
  // the body of catch block
}

here

  • type1, type2, …, typeN – accordingly, the argument type argument1, argument2, …, argumentN.

The code to be monitored must be executed in the middle of the try block. Exceptions are caught by the catch statement, which immediately follows the try block in which they occurred.

In a try block, operators and functions can be placed. If a corresponding exception is generated in the try block, then it is intercepted by the corresponding catch block. The choice of a catch block is carried out depending on the type of exception. After an exception of a certain type occurs, a catch block is called with the same type of argument. The argument takes some value, which is processed accordingly (an error message is displayed on the screen, etc.).

If an exception occurs in the try block that is not provided by the catch block, the standard terminate() function is called, which by default will call the abort() function. This standard function stops the execution of the program.

 

5. Operator throw

To throw an exception in the try block, you need to use the throw statement. The throw statement can be called inside a try block or inside a function that is called from a try block.

The general form of the throw statement is as follows

throw exception;

As a result of executing the throw statement, an exception of some kind is generated. This situation should be handled in the catch block.

 

6. Examples of using the try…catch block

Example 1. Demonstrated using a try…catch block to process an expression:

In this expression, in three cases an exception may occur:

  • root of a negative number a, if a<0;
  • root of a negative number b, if b<0;
  • division by 0, if b = 0.

Therefore, in the try…catch block, you need to handle these three cases. The text of a program like Console Application is as follows:

#include <iostream>
using namespace std;

void main()
{
  // processing of expression sqrt(a)/sqrt(b)
  double a, b;
  cout << "a = ";
  cin >> a;

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

  double c;

  try { // the beginning of try block
    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) // error catching
  {
    if (e == 1)
      cout << "Division by 0." << endl;
    if (e == 2)
      cout << "Negative root." << endl;
  }
}

The result of the program

a = 5
b = 0
Division by 0.

After applying the try…catch block, the program does not stop working.

Example 2. Another option for processing the expression from example 1. Here the try…catch block contains two catch statements.

#include <iostream>
using namespace std;

void main()
{
  // processing the expression sqrt(a)/sqrt(b) - way 2
  double a, b;
  cout << "a = ";
  cin >> a;

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

  double c;
  string s;

  try {
    if (b == 0)
      throw "Division by 0.";
    if (b < 0)
      throw "Negative root.";
    if (a < 0)
      throw "Negative root.";

    // if there are no exceptions, then continue to calculate
    c = sqrt(a) / sqrt(b);
    cout << "c = " << c << endl;
  }
  catch (int e) // catching an error of type int
  {
    if (e == 1)
      cout << "Division by 0." << endl;
    if (e == 2)
      cout << "Negative root." << endl;
  }
  catch (const char* e) // catching an error of type const char*
  {
    cout << e << endl;
  }
}

 



7. An example of using the try…catch block in a function

Task. Write a function for calculating a value from a given string of characters, which is a record of this number in the decimal system. To foresee the case of going beyond the bounds of the range defined by the int type. An exception mechanism must be used. The text of the program for an application such as Console Application is as follows

// Function for calculating a value from a given string of characters
int StrToInt(const char* str)
{
  char s[20];
  int t, i;
  long res = 0; // the result of the returning from function
  int len = strlen(str);

  try {
    t = 1;
    if (str[0] == '-') t = -1; // check if the first character is a '-'

    // loop to convert a string to an int
    i = len - 1;
    while (i >= 0)
    {
      if (str[i] == '-')
      {
        // if the first character is '-', then everything is fine
        if (i == 0) break;
        else throw "Bad position of minus.";
      }

      // if there are invalid characters in the string, then throw an exception
      if (str[i] < '0') throw "Bad symbols";
      if (str[i] > '9') throw "Bad symbols";

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

    // if the result is out of range for 32-bit integer values,
    // then throw appropriate exceptions
    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;
}

The above program can be rewritten so that the try…catch block is placed in the main() function, as shown below

// Function for calculating a value from a given string of characters
int StrToInt2(const char* str)
{
  char s[20];
  int t, i;
  long res = 0; // the result of function
  int len = strlen(str);

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

  i = len - 1;
  while (i >= 0)
  {
    if (str[i] == '-')
    {
      if (i == 0) break; // if the first character '-', it is all right
      else throw "Bad position of minus."; // otherwise throw an exception
    }
    if (str[i] < '0') throw "Bad symbols";
    if (str[i] > '9') throw "Bad symbols";

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

  // if the result is out of range for 32-bit integer values,
  // then throw appropriate exceptions
  if (res > INT32_MAX)
    throw "Out of range.";
  if (res < INT32_MIN)
    throw "Out of range.";
  return res;
}

void main()
{
  int d;

  // try...catch block is placed in the main() function of the highest level,
  // and an exception is thrown in the lower level function StrToInt2()
  try {
    d = StrToInt2("19125");
    cout << "d = " << d;
  }
  catch (const char* e)
  {
    cout << e << endl;
  }
}

As you can see from the above code, you can throw exceptions with the throw operator in another function whose call is included in the try block. This means that a function in its body can throw an exception.

The result of the program

d = 19125

If the call to the StrToInt2() function is moved outside the try statement

void main()
{
  int d;

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

  // calling a function outside the try statement
  d = StrToInt2("в19125");
}

then exceptions in the StrToInt2() function will not be processed. When an exception occurs in the StrToInt2() function, the compiler will generate its own error

Exception Unhandled

 

8. An example of program that throws an exception in one function and catches it in another function

In the example, a const char* exception is thrown in the lower-level function GenerateException(). The function checks the valid boundaries of the input parameter index.

In the top-level function ProcessException(), the GenerateException() function is called. This call is taken in a try block.

The text of the program is as follows:

// Example of a program that throws an exception in one function and catches it in another
// The function throws an exception "Out of index",
// if index is outside the range of 0..9
void GenerateException(int index)
{
  if ((index < 0) || (index > 9))
    throw "Out of index";
}

// Function that catches the exception "Out of index"
void ProcessException()
{
  int index;
  cout << "index = ";
  cin >> index;

  // 1. Cause an exception without processing,
  // the compiler will give the message "Exception unhandled"
  // GenerateException(-3);

  // 2. Cause a try ... catch block with processing exception
  try {
    GenerateException(index); // calling a function that throws an exception
    cout << "OK!" << endl; // if index is within 0..9, then OK!
  }
  catch (const char* e)
  {
    cout << "Error: " << e << endl;
  }
}

void main()
{
  ProcessException();
}

The result of the program

index = -5
Error: Out of index

 

9. Using the catch(…) block. Catching of all possible exceptional situations. Example

There are times when you need to intercept all exceptional situations in a row. For this, C++ uses the catch(…) block, which has the following general form

catch(...)
{
  // Processing of all exceptions
  // ...
}

Example. The example demonstrates the use of a catch(…) block to handle situations of any type.

The program implements:

  • function DivNumbers(), which returns the result of dividing two numbers entered from the keyboard. An int type exception is thrown in the function if the divisor value is 0;
  • function SqRoot() that returns the root of a negative number. An exception of type const char* is thrown in the function if the value of the number parameter is negative;
  • Function ProcessException(). This function demonstrates how DivNumbers() and SqRoot() functions work. The function uses the try…catch statement.
// Example. Demonstration of using the catch block
// A function that divides 2 numbers and returns the result
double DivNumbers(double a, double b)
{
  if (b == 0) throw 1;
    return a / b;
}

// Function that returns the root of a negative number
double SqRoot(double number)
{
  if (number < 0) throw "Negative number";
    return sqrt(number);
}

// Demonstration of catch(...) block
void ProcessException()
{
  double v;

  // display and call loop of the desired function
  while (1)
  {
    cout << "Input a function to call (1-2, 3-exit): " << endl;
    cout << "1-DivNumbers. 2-SqRoot" << endl;
    cout << ">>";
    cin >> v;

    // Invoke various function options
    try {
      if (v == 1) // function DivNumbers()
      {
        double a, b;
        cout << "DivNumbers(double a, double b)" << endl;

        // input a, b
        cout << "a = "; cin >> a;
        cout << "b = "; cin >> b;

        // call the function 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;

        // call the function SqRoot()
        x = SqRoot(num);
        cout << "x = " << x << endl;
      }
      if (v == 3) break;
    }
    catch (const char* e)
    {
      cout << "Error. Text = " << e << endl;
    }
    catch (...) // and othe exceptions types
    {
      cout << "Error in block catch(...)." << endl;
    }
  }
}

void main()
{
  ProcessException();
}

As you can see from the text of the ProcessException() function, the call to the DivNumbers() and SqRoot() functions is taken in a try…catch block

// Invoke different options for functions
try {
  ...
}
catch (const char* e)
{
  cout << "Error. Text = " << e << endl;
}
catch (...) // and others exceptions types
{
  cout << "Error in block catch(...)." << endl;
}

In the try…catch block are processed

  • the exception of type const char*;
  • other types of exceptions.

In this case, the catch(…) statement is used.

The result of the program

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

 


Related topics