Home C++ Introduction Decisions Loops Input/Output Functions Stack and Heap References Arrays Searching and Sorting Recursion Pointers Character and Strings Structures Classes Inheritance Exceptions Templatess STL Modern C++ Misc Books ----


Exceptions


Contents

Procedural Style

In a procedural language the error handling is either done by
checking the return value of a function or checking some other
variable that contains the error. The pseudo code can look like:

      result = function1() ;

      if ( result < 0 )

         //Handle error

      function2() ;

       if ( ErrorCode == 100 )

          //Handle error

The code can easily become very messy. Another way to handle errors and this is
usually a feature in object oriented programming is to use Exceptions. The above
will instead look like:

          try
          {
               result = function1() ;
               function2() ;
          }
         catch ( Exception except )
         {
              //Any error in the try block causes control to come
              //to the catch block.
         }

 The code that can "throw" an error is enclosed in a "try" block. Immediately
 following the "try" block we have a "catch" block. In the "catch" block we
 can handle the error. We  need to have the "catch" following the "try" block else
 we will get a compiler error. Both the "try" and "catch" work hand in hand.

Object Oriented Style

Basics


File: except1.cpp
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int ErrorCode = 0 ;
int divideOperation1( int x1, int y1 )
 {
     ErrorCode = 0 ;
     if ( y1 == 0 )
       {
           ErrorCode = -1 ;
           return 0 ;
        }
    return ( x1 / y1 ) ;
 }

int divideOperation2( int x1, int y1 )
{
    ErrorCode = 0 ;
    if ( y1 == 0 )
      {
         throw  "Divide by zero." ;
      }
     return ( x1 / y1 ) ;
}

int main()
{
    divideOperation1 ( 4 ,  0 ) ;
    if ( ErrorCode == -1 )
      cout << "Error divisor is zero." << endl ;

        try
         {
              divideOperation2 ( 4 ,  0 ) ;
         }
        catch ( const char* str1 )
        //catch ( const string& str1 )
        {
            cout << str1  << endl ;
        }
    return 0;
}
Output:

$ ./a.exe

Error divisor is zero.

Divide by zero.

This program shows the old way of handling errors using the function:
"divideOperation1" and the new way in the function ""divideOperation2" .

    if ( y1 == 0 )
      {
         throw  "Divide by zero." ;
      }

 This code is inside the function "function2" . What happens when
 "throw" is encountered ? In this case there is no catch otherwise control
 will move to the catch part. The system will clean up any local objects
 and the function will not execute any more code and the exception being
 thrown "const char*" is thrown to the calling code.

	    try
	     {
			  divideOperation2 ( 4 ,  0 ) ;
		 }
		//catch ( const string& str1 )
		catch ( const char* str1 )
		{
			cout << str1  << endl ;
		}

The "divideOperation2" in the calling code is enclosed in a "try" block so if it throws
an exception the control comes to the catch block. If the object being thrown matches
the catch argument then the catch code is executed. If the argument is not caught
then it will be passed to the calling function. However in the above case we are at the
"main" function and so the program will abort as demonstrated by the below program.


File: except2.cpp
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int ErrorCode = 0 ;
int divideOperation1( int x1, int y1 )
 {
     ErrorCode = 0 ;
     if ( y1 == 0 )
       {
           ErrorCode = -1 ;
           return 0 ;
        }
    return ( x1 / y1 ) ;
 }

int divideOperation2( int x1, int y1 )
{
    ErrorCode = 0 ;
    if ( y1 == 0 )
      {
         throw  "Divide by zero." ;
         //throw  String("Divide by zero.") ;
      }
     return ( x1 / y1 ) ;
}

int main()
{
    divideOperation1 ( 4 ,  0 ) ;
    if ( ErrorCode == -1 )
      cout << "Error divisor is zero." << endl ;

      divideOperation2 ( 4 ,  0 ) ;
      cout << "End of Main."  << endl ;

    return 0;
}
$ g++ except2.cpp ; ./a.exe
Error divisor is zero.
terminate called after throwing an instance of 'char const*'
Aborted (core dumped)

In the above code we have modified "except2.cpp" to have the
statements:

      divideOperation2 ( 4 ,  0 ) ;
      cout << "End of Main."  << endl ;

Since the exception is not caught and we are in the "main" function the
system will not execute any more statements
( "End of Main" is not executed )
and the program will abort as
shown in the output.
There is a slight disadvantage to the exception handling and that is the control moves to the catch block and it may be difficult to retry the function calls as we are out of the normal coding block.

Multiple Catch


We can also have multiple catch statements corresponding to our try block.

File: except3.cpp
#include <iostream>
#include <fstream>

using namespace std;

int ErrorCode = 0 ;
int divideOperation1
( int x1, int y1 )
{
   ErrorCode = 0 ;
   if ( y1 == 0 )
   {
    ErrorCode = -1 ;
    return 0 ;
   }
   return ( x1 / y1 ) ;
 }

 int divideOperation2( int x1, int y1 )
 {
     ErrorCode = 0 ;
     if ( y1 == 0 )
      {
           throw "Divide by zero." ;
      }
     return ( x1 / y1 ) ;
 }

 int main()
 {
     divideOperation1 ( 4 ,  0 ) ;
     if ( ErrorCode == -1 )
       cout << "Error divisor is zero." << endl ;
       // throw 1 ;
     try
     {
         throw 5 ;
         //throw statement can occur anywhere
         divideOperation2 ( 4 ,  0 ) ;
     }
     catch ( const char* str1 )
     {
         cout << str1  << endl ;
     }
     //Reference to avoid a copy
     catch ( int& e1 )
     {
         cout << "Integer Exception"   << endl ;
         return 1 ;
     }
     return 0;
}
Output:
$ g++ except3.cpp ; ./a.exe
Error divisor is zero.
Integer Exception./a.exe

We have 2 catch statements that correspond to the try block. One is to catch strings and the other
is to catch an integer. The "divideOperation2" with the divisor of zero in this case would have
thrown an exception but we never reach that line because the
"throw 5"
is executed.


Nested Catch

We can have nested try catch blocks also.
File: except4.cpp
#include <iostream>
#include <fstream>

using namespace std;

void function2()
{
    throw 10 ;
}

void function1()
{
        function2() ;
}

int main()
{
            try
            {
                try
                {
                    function1() ;
                }
                catch ( int& e1 )
                {
                        cout << "Integer Exception"   << endl ;
                        throw e1 ;
                }
            }
            catch( int& e2 )
            {
                cout << "Outer Integer Exception"   << endl ;
            }
    return 0;
}
Output:

$ g++ except4.cpp ; ./a.exe

Integer Exception
Outer Integer Exception
Exercises
1)
File: ex1.cpp

2)
File: ex2.cpp

General Exception

We can use the "..." to catch an exception of any type .
File: except5.cpp
#include <iostream>
#include <fstream>

using namespace std;
int main()
{
    try
    {
        throw "Some error" ;
    }
    catch ( int& e1 )
    {
        cout << "Integer Exception"   << endl ;
    }
    catch( ...  )
    {
        cout << "Default Handler."   << endl ;
    }
    return 0;
}
Output:

$ g++ except5.cpp ; ./a.exe

Default Handler.


The notation
catch( ...  )

means the catch can handle any type. We can also catch other
specific exceptions as shown in the code at the same time.
This may be useful if we are calling other functions that


Custom Exception

We can also throw an object of our own class.
File: except6.cpp
#include <iostream>
#include <fstream>

using namespace std;

class MyException 
{ 
 public:    
  string messg ;  
  MyException( string str1 )
   { 
    messg = str1 ; 
   }
};

int main()
{  
 try 
  {   
    throw MyException("Error") ; 
  } 
 catch ( MyException& e1 ) 
  { 
    cout << e1.messg   << endl ; 
  }  
 return 0;
 
}
$ g++ except6.cpp ; ./a.exe
Error

Notice we have been using the reference in the catch statement

catch ( MyException& e1 )

The below program shows the difference between using regular objects
instead of references.



File: scope1.cpp
#include <iostream>
#include <fstream>

using namespace std;

class MyException
{
 public:
  string messg ;
  MyException( string str1 )
   {
     messg = str1 ;
     cout << "MyException: constructor: " << messg << endl ;
   }

  MyException( const MyException& obj1 )
   {
     messg = obj1.messg  ;
     cout << "MyException: copy constructor: " << messg << endl ;
   }

   ~MyException(  )
    {
      cout << "MyException: destructor: " << messg << endl ;
    }
};

int main()
{
     try
      {
        MyException  error1("Error 1:")  ;
        throw MyException("Error 2:") ;
      }
     catch ( MyException error2 )
      {
        cout << error2.messg   << endl ;
      }
    cout << "Step 1:" << endl ;

    try
     {
             throw MyException("Error 3:") ;
     }
    catch(  MyException& error3 )
     {
        cout << error3.messg   << endl ;
     }

  cout << "Step 2:" << endl ;

 return 0;

}

$ g++ scope1.cpp ; ./a.exe
MyException: constructor: Error 1:
MyException: constructor: Error 2:
MyException: destructor: Error 1:
MyException: copy constructor: Error 2:
Error 2:
MyException: destructor: Error 2:
MyException: destructor: Error 2:
Step 1:
MyException: constructor: Error 3:
Error 3:
MyException: destructor: Error 3:
Step 2:


The lines get executed first.

	 try
	  {
		MyException  error1("Error 1:")  ;
		throw MyException("Error 2:") ;
	  }
	 catch ( MyException error2 )
	  {
		cout << error2.messg   << endl ;
	  }

MyException  error1("Error 1:")  ;
This creates the "error1" object and the statement:

MyException: constructor: Error 1:

Then we hit the "throw" statement:

throw MyException("Error 2:") ;

This will create an object and we get the below statement
printed out:

MyException: constructor: Error 2:

We come to the handler but before doing that we need to clean up the local
objects in the "try" block and the destructor for "error1" object get called.

	 catch ( MyException error2 )
	  {
		cout << error2.messg   << endl ;
	  }

The object with the message "Error 2" from the try block
is copied to "error2" object.

MyException: copy constructor: Error 2:

We print out the message "Error 2:"  and the come out of the handler.
This causes the cleanup of the 2 "Error 2" objects: one that was thrown and
the other one that was in the argument to the catch handler.

We print out a debug statement and then hit the lines:

    try
     {
     		throw MyException("Error 3:") ;
	 }
	catch(  MyException& error3 )
	 {
		cout << error3.messg   << endl ;
	 }


Step 1:
MyException: constructor: Error 3:
Error 3:
MyException: destructor: Error 3:
Step 2:

We create the "Error 3:" object in the "try" block and then
jump to the handler. Since the argument "error3" is a reference we do
not create another object and only 1 constructor is called as shown in the output.

Type Conversion

We need to be careful when stating the type of object in the "catch" handler. There is no implicit conversion performed. The exact type that was specified in the throw statement must be used.
File: construct1.cpp
#include <iostream>
#include <fstream>
#include <string>

using namespace std;


int divideOperation2( int x1, int y1 )
{
    if ( y1 == 0 )
      {
         cout << "Step1" << endl ;
         throw  "Divide by zero." ;
         //throw  String("Divide by zero.") ;
      }
     return ( x1 / y1 ) ;
}

void method1( string str1 )
{
   cout << "Inside method1: " <<  str1 << endl   ;

}


int main()
{


        try
         {
              string str1 = "Divide by zero." ;
              method1( str1 ) ;
              const char* str2 =  "Divide by zero str2."  ;
              method1( str2 ) ;
              string str3 = str2 ;
              divideOperation2 ( 4 ,  0 ) ;
         }
        catch ( const string& str1 )
        //catch ( const char* str1 )
        {
            cout << "Step2" << endl ;
            cout << str1  << endl ;
        }
        cout << "Step3" << endl ;

    return 0;
}

$ g++ construct1.cpp ; ./a.exe
Inside method1: Divide by zero.
Inside method1: Divide by zero str2.
Step1

The below code shows how a "const char*" can be implicitly converted to
a string type. However the handler that contains a string object does not get
called because the "const char*" is not implicitly converted to a string.

			  string str1 = "Divide by zero." ;
			  method1( str1 ) ;
			  const char* str2 =  "Divide by zero str2."  ;
			  method1( str2 ) ;

Inheritance

If we have a hierarchy then we can throw a derived exception and catch it with the base class.
File: except8.cpp
#include <iostream>
#include <fstream>

using namespace std;

class MyExceptionBase 
{    
  public: 
   string messg ; 
   MyExceptionBase( string str1 ) 
    {     
       messg = str1 ; 
     }
};

class MyException : public MyExceptionBase
{ 
  public: 
    MyException( string str1 ) : MyExceptionBase( str1 )
    {
    }
};

int main()
{  
    try
      { 
        throw MyException("Error") ; 
      } 
    catch ( MyExceptionBase& e1 )
      { 
         cout << e1.messg   << endl ; 
      } 
     return 0;
}
$ g++ except8.cpp ; ./a.exe
Error
If we list the classes in a hierarchy we need to make sure we list the derived classes first. The following produces a warning.
File: except9.cpp
#include <iostream>
#include <fstream>

using namespace std;

class MyExceptionBase
{
  public:
   string messg ;
   MyExceptionBase( string str1 )
    {
       messg = str1 ;
     }
};

class MyException : public MyExceptionBase
{
  public:
    MyException( string str1 ) : MyExceptionBase( str1 )
    {
    }
};

int main()
{
    try
      {
        throw MyException("Error") ;
      }

    catch ( MyExceptionBase& e1 )
      {
         cout << e1.messg   << endl ;
      }

    catch ( MyException& e1 )
      {
         cout << "Derived" << e1.messg   << endl ;
      }
     return 0;
}
$ g++ except9.cpp ; ./a.exe
except9.cpp: In function �int main()�:
except9.cpp:36:5: warning: exception of type �MyException� will be caught by earlier handler [-Wexceptions]
   36 |     catch ( MyException& e1 )
      |     ^~~~~
except9.cpp:31:5: note: for type �MyExceptionBase�
   31 |     catch ( MyExceptionBase& e1 )
      |     ^~~~~
Error
It should be written as:
File: except10.cpp
#include <iostream>
#include <fstream>

using namespace std;

class MyExceptionBase
{
  public:
   string messg ;
   MyExceptionBase( string str1 )
    {
       messg = str1 ;
     }
};

class MyException : public MyExceptionBase
{
  public:
    MyException( string str1 ) : MyExceptionBase( str1 )
    {
    }
};

int main()
{
    try
      {
        throw MyException("Error") ;
      }


    catch ( MyException& e1 )
      {
         cout << e1.messg   << endl ;
      }

    catch ( MyExceptionBase& e1 )
       {
          cout << e1.messg   << endl ;
       }

  return 0;
}
$ g++ except10.cpp ; ./a.exe
Error

Standard Exceptions


C++ provides standard exceptions that can be used to catch exceptions thrown by the language.
File: mem1.cpp
$ g++ mem1.cpp ; ./a.exe
Standard exception: std::bad_alloc

We can also derive our own class from the exception class.
File: mem2.cpp
Exception specifications allow us to specify if a method throws an exception or not. In the below code the "method3" is called but the exception is not caught even though there is a try catch block. This is because the "method3" threw an exception even though we declared that it does not do so.
File: except11.cpp