Exception Handling In C++

By Sruthy

By Sruthy

Sruthy, with her 10+ years of experience, is a dynamic professional who seamlessly blends her creative soul with technical prowess. With a Technical Degree in Graphics Design and Communications and a Bachelor’s Degree in Electronics and Communication, she brings a unique combination of artistic flair…

Learn about our editorial policies.
Updated March 7, 2024

Exception Handling In C++ For Making The Code Robust And Efficient.

In any programming language, when we develop programs, we cannot guarantee that they will be error-free. At some point, we are guaranteed to encounter an error.

The errors can be classified into two types:

  • Compile time errors: These are the errors that occur during the compile time. Most usual compile-time errors are syntax errors, or incorrect include/imports or incorrect references.
  • Runtime errors: These errors occur at runtime and are called exceptions.

=> Watch Out The Beginners C++ Training Guide Here.

Exception Handling in C++

Errors occurring at runtime create serious issues and most of the time, it hinders the normal execution of the program. That is why we have to handle these errors. This handling of errors in order to maintain the normal flow of the execution of the program is known as “Exception Handling”.

Exception Handling In C++

In C++, exception handling is provided by using three constructs or keywords; namely, try, catch and throw.

Block of code that provides a way to handle the exception is called “exception handler”.

The general syntax of a typical exception handler is:

try{
                 //code likely to give exception
}catch(exception e){
                 //code to process exception e
}

The code that is likely to throw exceptions is enclosed in the “try” block. The try block can also contain a “throw” statement that is used to explicitly throw exceptions. The “throw” statement consists of a keyword “throw” followed by a parameter which is the exception name. This parameter is then passed to the catch block.

The exception thrown in the “try” block is passed on to the “catch” block. The catch block contains the code to process the thrown exception. It can contain merely a message or an entire code block to process the exception such that the normal flow of the program is not hindered.

It is not necessary that each try block should be followed by only one catch block. If our try block code contains the statements that can throw more than one exceptions, then we can have multiple catch blocks wherein each catch block provides processing for each of the exceptions.

In this case, the structure for an exception handler looks as shown below:

try{
}catch(exception ex1)
{}
catch(exception ex2)
{}

…..
catch(exception exn)
{}

Next, we will see the exceptions supported by C++ along with their description.

C++ Standard Exceptions

The following diagram shows the hierarchy of exceptions class std:: exception supported in C++.

hierarchy of exceptions in C++

In the below table, we will see the description of each of the above exceptions:

ExceptionDescription
std::exceptionThis is the parent class for all C++ standard exceptions.
std::bad_allocThrown by ‘new’ operator when memory allocation goes wrong.
std::bad_castOccurs when ‘dynamic_cast’ goes wrong.
std::bad_typeidThis exception is thrown by typeid.
std::bad_exceptionThis exception is used to handle unexpected exceptions in the program.
std::runtime_errorExceptions that occur at runtime and cannot be determined merely by reading the code.
std::overflow_errorThis exception is thrown when mathematical overflow occurs.
std::underflow_errorThis exception is thrown when mathematical underflow occurs.
std::range_errorWhen program stores value that is out of range, this exception is thrown.
std::logic_errorThose exceptions can be deduced by reading the code.
std::out_of_rangeThe exception is thrown when we access/insert elements that are not in range. For example, in the case of vector or arrays.
std::length_errorThis exception is thrown in the case when the length of the variable is exceeded. For example, when a string of big length is created for type std::string.
std::domain_errorThrown when the mathematically invalid domain is used.
std::invalid_argumentException usually thrown for invalid arguments.

Using Try, Catch And Throw

We have already seen how the try, catch and throw statements are used in exception handling in C++. Now, let us see a programming example to understand their usage better.

Here, we have presented a classic case of runtime error “divide by zero”. The program will compile fine as there are no logical errors. But at runtime, as the denominator provided is ‘0’, the program will crash for sure.

In order to prevent this, we put the division code in a try block and process the exception in the catch block such that the program will not crash and will execute normally.

#include <iostream>
using namespace std;

int main()
{
                int numerator = 20, denominator = 0;
 
                try{
                                if(denominator == 0)
                                               throw "Exception - Division by zero!!";
                                else
                                               cout<<"Result = "<<numerator/denominator;
               }
               catch(const char* error) {
                            cout << error << endl;
              }
 
}

Output:

Exception – Division by zero!!

The above program is a simple illustration of “try throw catch” in C++. As we see, we provide numerator and denominator values in the program. Next, we check if the denominator is zero. If it is, then we throw the exception otherwise we print the result. In the catch block, we print the error message thrown by the exception.

This ensures that the program will not crash or abnormally terminate when it encounters division by zero.

How To Stop Infinite Loop In Exception Handling

Let us consider yet another case of an infinite loop.

If the code inside is bound to throw an exception, then we need to consider the actual place where we should put our try, catch block. That is whether the try catch block should be inside the loop or the loop should be inside the try-catch block.

There is no guaranteed answer regarding the placement of try catch block, however, it depends solely on the situation. One consideration that we have to give is that the try catch block usually causes overhead for the program. So unless required, we should do away with an exception handling code in the infinite loop.

Let us consider the example shown below.

#include <iostream>
#include <stdexcept>
using namespace std;

int main (void)
 {
   int Sum=0, num;
   cout<<"Please enter number you wish to add(-99 to exit):"<<endl; while( true ) { try{ cin>>num;
         if(num == -99)
              throw -99;
         Sum+=num;
      }
     catch(...)
     {
         cout <<" Aborting the program..."<<endl;
         break;
      }
  }
cout << "Sum is:" << Sum << endl;
return 0;
 }

Here we have an infinite while loop. Inside the loop, we read an input number and add it to sum. In order to come out of the loop, we need to give the terminating condition inside the loop. We have given -99 as the terminating condition.

As seen, we have put this code in a try block and when the number entered is equal to -99, we throw an exception that is caught in the catch block.

Now in the above situation, if we put the entire while loop inside the try block, then there is bound to be overhead as while is an infinite loop. So the best place to put the try catch block is inside the loop.

The program gives the following output:

Output:

Please enter the number you wish to add(-99 to exit):
-99
Aborting the program…
Sum is:0

Note that the control is passed to catch block once -99 is entered as input.

Note that we have not given any specific exception object as an argument to catch. Instead, we have provided (…). This indicates that we are catching the generalized exception.

Stack Unwinding

We are aware that whenever a program is modularized and function calls are involved, the current state of the program is saved on a stack. When the function returns back, the state is restored and program execution is continued normally.

Stack unwinding is generally associated with exception handling. It is a process in which function entries are removed from the call stack at run time. In case of C++, whenever an exception occurs, the call stack where function entries are stored is searched linearly for an exception handler.

All the entries are removed from the stack till a function entry with the exception handler is found. This means stack unwinding takes place when an exception is not handled in the same function i.e. a function throws an exception but does not provide the catch handler.

Let us understand the stack unwinding process using a programming Example.

#include <iostream>
using namespace std;

void last_f()
{
   cout << "last_f :: start\n";
   cout << "last_f:: throw int exception\n";
   throw -1;
   cout << "last_f :: end\n";
}
 
void third_f()
{
   cout<< "third_f :: Start\n";
   last_f();
   cout << "third_f :: End\n";
}
 
void second_f()
{
   cout << "second_f :: Start\n";
   try {
      third_f();
  }
catch(double) {
   cout << "second_f :: catch double exception\n";
  }
cout << "second_f :: End\n";
}
 
void first_f()
{
    cout << "first_f :: Start\n";
    try {
       second_f();
    }
    catch (int) {
          cout << "first_f :: catch int exception\n";
   }
   catch (double) {
         cout << "first_f :: catch double exception\n";
  }
cout << "first_f :: End\n";
}
 
int main()
{
   cout << "main :: Start\n";
   try
  {
       first_f();
  }
catch (int)
{
      cout << "main :: catch int exception\n";
}
cout << "main :: End\n";
 
return 0;
}

Output:

main :: Start
first_f :: Start
second_f :: Start
third_f :: Start
last_f :: start
last_f:: throw int exception
first_f :: catch int exception
first_f :: End
main :: End

In the above program, we have four functions which are called from each other.

The function call path is main=>first_f()=>second_f()=>third_f()=>last_f().

Hence the first five lines of output are self-explanatory.

In the last_f function, an int exception is thrown. But there is no catch block in this function and thus the stack begins to unwind. Thus last_f is terminated and control is passed to the caller function that is third_f. In this function as well there is no exception handler and so the third_f is terminated and control is passed to second_f.

There is an exception handler in the second_f function, but it does not match with the int exception and the function is terminated.

Next, the control passes to the caller function first_f. In this function, the int exception thrown by the last_f function matches with the exception handler provided. This catch handler is called and the message is printed. Then the statement following catch handler is printed and the control returns to the main function.

In the main as well, there is a handler for int exception but as it’s already completed, the control then passes to the last statement in the main function and the last statement is printed.

Hence in the output of the program, we see that as there was no exception handler that matched the int exception, stack unwound itself till the function first_f. As a result, the functions preceding first_f function were terminated and not completed.

Thus through stack unwinding, C++ provides us an advantage to place the exception handler at an appropriate place. So even if the function just throws an exception and doesn’t want to handle it, an exception will propagate itself till it finds an appropriate exception handler.

Out_of_range Exception

The exception of “out_of_range” is the most common exception that a programmer encounters. Hence it needs a special mention in this tutorial.

Sometimes when we are using containers like arrays or vectors, we define their size or number of elements that they contain at the compile time. So when a programmer tries to insert or access the elements outside this size, then an “out_of_range” exception is thrown.

Consider the following programming Example to understand this:

#include <iostream>
#include <stdexcept>
#include <vector>
using namespace std;

int main (void)
 {
 vector<int> myvector(10);
 try {
    myvector.at(11)=11;
 }
catch (const out_of_range& oor_exce) {
    cout << "Out of Range Exception: " << oor_exce.what() << '\n';
 }
return 0;
 }

Output:

Out of Range Exception: vector::_M_range_check: __n (which is 11) >= this->size() (which is 10)

Screenshot for the same is shown below:

Out_of_range exception output

In the above program, we have a vector container of size 10. Next, we try to insert element 11 at the 11th location in the vector. As the vector is declared to be of size 10, an out of range exception occurs which is caught in the catch block of the program and the exact reason given by the “what” function is printed along with the message.

Conclusion

In this tutorial, we have seen exception handling in C++. Readers should know that exception handling is an integral part of the program. It ensures the normal execution of the program so that when an error or exceptional situation occurs, the program does not die.

Thus while using exceptions, one should be able to use them cleverly to make the code robust and efficient. We will come up with more important topics in our subsequent tutorials.

=> See Here To Explore The Full C++ Tutorials list.

Was this helpful?

Thanks for your feedback!

Leave a Comment