Exception Handling in C++
Last Updated :
08 Mar, 2024
In C++, exceptions are runtime anomalies or abnormal conditions that a program encounters during its execution. The process of handling these exceptions is called exception handling. Using the exception handling mechanism, the control from one part of the program where the exception occurred can be transferred to another part of the code.
So basically using exception handling in C++, we can handle the exceptions so that our program keeps running.
What is a C++ Exception?
An exception is an unexpected problem that arises during the execution of a program our program terminates suddenly with some errors/issues. Exception occurs during the running of the program (runtime).
Types of C++ Exception
There are two types of exceptions in C++
- Synchronous: Exceptions that happen when something goes wrong because of a mistake in the input data or when the program is not equipped to handle the current type of data it’s working with, such as dividing a number by zero.
- Asynchronous: Exceptions that are beyond the program’s control, such as disc failure, keyboard interrupts, etc.
C++ try and catch
C++ provides an inbuilt feature for Exception Handling. It can be done using the following specialized keywords: try, catch, and throw with each having a different purpose.
Syntax of try-catch in C++
try {
// Code that might throw an exception
throw SomeExceptionType("Error message");
}
catch( ExceptionName e1 ) {
// catch block catches the exception that is thrown from try block
}
1. try in C++
The try keyword represents a block of code that may throw an exception placed inside the try block. It’s followed by one or more catch blocks. If an exception occurs, try block throws that exception.
2. catch in C++
The catch statement represents a block of code that is executed when a particular exception is thrown from the try block. The code to handle the exception is written inside the catch block.
3. throw in C++
An exception in C++ can be thrown using the throw keyword. When a program encounters a throw statement, then it immediately terminates the current function and starts finding a matching catch block to handle the thrown exception.
Note: Multiple catch statements can be used to catch different type of exceptions thrown by try block.
The try and catch keywords come in pairs: We use the try block to test some code and If the code throws an exception we will handle it in our catch block.
Why do we need Exception Handling in C++?
The following are the main advantages of exception handling over traditional error handling:
- Separation of Error Handling Code from Normal Code: There are always if-else conditions to handle errors in traditional error handling codes. 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.
- 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).
- 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.
Examples of Exception Handling in C++
The following examples demonstrate how to use a try-catch block to handle exceptions in C++.
Example 1
The below example demonstrates throw exceptions in C++.
C++
#include <iostream>
#include <stdexcept>
using namespace std;
int main()
{
try {
int numerator = 10;
int denominator = 0;
int res;
if (denominator == 0) {
throw runtime_error(
"Division by zero not allowed!" );
}
res = numerator / denominator;
cout << "Result after division: " << res << endl;
}
catch ( const exception& e) {
cout << "Exception " << e.what() << endl;
}
return 0;
}
|
Output
Exception Division by zero not allowed!
Example 2
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)
Properties of Exception Handling in C++
Property 1
There is a special catch block called the ‘catch-all’ block, written as catch(…), that can be used to catch all types of exceptions.
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;
}
|
Property 2
Implicit type conversion doesn’t happen for primitive types.
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
Property 3
If an exception is thrown and not caught anywhere, the program terminates abnormally.
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'
We can change this abnormal termination behavior by writing our unexpected function.
Note: A derived class exception should be caught before a base class exception.
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.
Property 4
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. However,exception-handling it’s a recommended practice to do so.
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;
}
|
Output
Caught exception from fun()
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().
Property 5
In C++, try/catch blocks can be nested. Also, an exception can be re-thrown using “throw; “.
Example
The following program shows try/catch blocks nesting.
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.
Property 6
When an exception is thrown, all objects created inside the enclosing try block are destroyed before the control is transferred to the catch block.
Example
The following program demonstrates the above property.
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
Limitations of Exception Handling in C++
The exception handling in C++ also have few limitations:
- Exceptions may break the structure or flow of the code as multiple invisible exit points are created in the code which makes the code hard to read and debug.
- If exception handling is not done properly can lead to resource leaks as well.
- It’s hard to learn how to write Exception code that is safe.
- There is no C++ standard on how to use exception handling, hence many variations in exception-handling practices exist.
Conclusion
Exception handling in C++ is used to handle unexpected happening using “try” and “catch” blocks to manage the problem efficiently. This exception handling makes our programs more reliable as errors at runtime can be handled separately and it also helps prevent the program from crashing and abrupt termination of the program when error is encountered.
Related Articles:
Please Login to comment...