In programming, mistakes that cause unusual conditions (errors) are common. These errors generally fall into three categories:
Exception handling in C++ is a mechanism to detect and manage runtime errors in a structured way.
Instead of terminating the program abruptly when an error occurs, C++ allows you to throw exceptions and catch them for graceful handling.
Common examples:
The main goal of exception handling is to keep regular program logic separate from error-handling logic. To handle exceptions, C++ uses try and catch blocks, where try contains the code that may throw an exception, and catch contains the code to handle it.
How Exception Handling Works in C++ 1. Throwing an ExceptionWhen an error or unexpected situation occurs, the program uses the throw
keyword to signal an exception.
The program searches for a matching catch
block to intercept and handle the thrown exception.
The catch
block contains the logic to respond to the error, allowing the program to recover or terminate gracefully.
C++ provides an inbuilt feature for handling exceptions using try and catch block. It is an exception handling mechanism where the code that may cause an exception is placed inside the try block and the code that handles the exception is placed inside the catch block.
Syntax C++
try {
// Code that might throw an exception
}
catch (ExceptionType e) {
// exception handling code
}
When an exception occurs in try block, the execution stops, and the control goes to the matching catch block for handling.
Throwing ExceptionsThrowing exception means returning some kind of value that represent the exception from the try block. The matching catch block is found using the type of the thrown value. The throw keyword is used to throw the exception.
C++
try {
throw val
}
catch (ExceptionType e) {
// exception handling code
}
There are three types of values that can be thrown as an exception:
Throwing built-in types is very simple but it does not provide any useful information. For example,
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
int x = 7;
try {
if (x % 2 != 0) {
// Throwing int
throw -1;
}
}
// Catching int
catch (int e) {
cout << "Exception Caught: " << e;
}
return 0;
}
Exception Caught: -1
Here, we have to make decision based on the value thrown. It is not much different from handling errors using if else. There is a better technique available in C++. Instead of throwing simple values, we can throw objects of classes that contains the information about the nature of exception in themselves.
Throwing Standard ExceptionsStandard exceptions are the set of classes that represent different types of common exceptions. All these classes are defined inside <stdexcept> header file and mainly derived from std::exception class which act as the base class for inbuilt exceptions. The below image shows standard exceptions hierarchy in C++:
These exceptions are thrown by C++ library components so we should know how to handle them. The what() method is present in every standard exception to provide information about the exception itself.
For example, the vector at() method throws an out_of_range exception when the element with given index does not exists.
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> v = {1, 2, 3};
try {
// Accessing out of bound element
v.at(10);
}
catch (out_of_range e) {
cout << "Caught: " << e.what();
}
return 0;
}
Caught: vector::_M_range_check: __n (which is 10) >= this->size() (which is 3)
We can also manually throw standard exceptions using throw statement.
Throwing Custom ExceptionsWhen the standard exceptions cannot satisfy our requirement, we can create a custom exception class. It is recommended to inherit standard exception in this class to provide seamless integrity with library components though, it is not compulsory.
Refer to the article to know more - Exception Handling using Classes
Catching ExceptionsAs already told, the catch block is used to catch the exceptions thrown in the try block. The catch block takes one argument, which should be of the same type as the exception.
C++
catch (exceptionType e) {
...
}
Here, e is the name given to the exception. Statements inside the catch block will be executed if the exception of exceptionType is thrown in try block.
Catching Multiple ExceptionsThere can be multiple catch blocks associated with a single try block to handle multiple types of exceptions. For example,
C++
try {
// Code that might throw an exception
}
catch (type1 e) {
// executed when exception is of type1
}
catch (type2 e) {
// executed when exception is of type2
}
catch (...) {
// executed when no matching catch is found
}
In the above code, the last statement catch(...) creates a catch-all block which is executed when none of the above catch statements are matched. For example,
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
// Code that might throw an exception
try {
int choice;
cout << "Enter 1 for invalid argument, "
<< "2 for out of range: ";
cin >> choice;
if (choice == 1) {
throw invalid_argument("Invalid argument");
}
else if (choice == 2) {
throw out_of_range("Out of range");
}
else {
throw "Unknown error";
}
}
// executed when exception is of type invalid_argument
catch (invalid_argument e) {
cout << "Caught exception: " << e.what() << endl;
}
// executed when exception is of type out_of_range
catch (out_of_range e) {
cout << "Caught exception: " << e.what() << endl;
}
// executed when no matching catch is found
catch (...) {
cout << "Caught an unknown exception." << endl;
}
return 0;
}
Output 1
Enter 1 for invalid argument, 2 for out of range: 2
Caught exception: Out of range
Output 2
Enter 1 for invalid argument, 2 for out of range: 1
Caught exception: Invalid argument
Output 3
Enter 1 for invalid argument, 2 for out of range: 10Catch by Value or Reference
Caught an unknown exception.
Just like function arguments, the catch block can catch exceptions either by value or by reference. Both of the methods have their own advantage.
Catch by Value
Catching exceptions by value creates a new copy of the thrown object in the catch block. Generally, the exceptions objects are not very large so there is not much overhead of creating copies.
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
try {
throw runtime_error("This is runtime exception");
}
// Catching by value
catch (runtime_error e) {
cout << "Caught: " << e.what();
}
return 0;
}
Caught: This is runtime exception
Catch by Reference
Catch by reference method just pass the reference to the exception thrown instead of creating a copy. Although it reduces the copy overhead, it is not the primary advantage of this method. The main advantage of this method is in catching polymorphic exception types. For example,
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
try {
throw runtime_error("This is runtime exception");
}
// Catching by value
catch (exception& e) {
cout << "Caught: " << e.what();
}
return 0;
}
Caught: This is runtime exception
In the above example, we were able to catch the runtime_error exception using the reference to exception class. This is very helpful in handling exceptions derived from other exceptions like in standard exception hierarchy.
Handle Uncaught ExceptionsIf the exception is not caught by any of the catch block (in case catch all is not present), then by default, the exception handling subsystem aborts the execution of the program. But this behaviour can be changed by creating a custom terminate handler and setting it as default using set_terminate() function. Refer to this article to know more - Customizing Termination Behaviour for Uncaught Exceptions
Exception PropagationException propagation tells us how the exception travels in the call stack.
Let's understand the flow using the following example:
C++
#include <bits/stdc++.h>
using namespace std;
// A dummy class
class GfG {
public:
GfG() {
cout << "Object Created" << endl;
}
~GfG() {
cout << "Object Destroyed" << endl;
}
};
int main() {
try {
cout << "Inside try block" << endl;
GfG gfg;
throw 10;
cout << "After throw" << endl;
}
catch (int e) {
cout << "Exception Caught" << endl;
}
cout << "After catch";
return 0;
}
Inside try block Object Created Object Destroyed Exception Caught After catch
In the above example,
In C++, try-catch blocks can be defined inside another try or catch blocks. For example,
C++
try {
// Code...... throw e2
try {
// code..... throw e1
}
catch (eType1 e1) {
// handling exception
}
}
catch (eType e2) {
// handling exception
}
If the exception thrown in the inner try block is of type eType2 instead of eType1, then it will be handled by the outer exception block. Refer to the given article to know more - Nested Try Blocks in C++
Rethrowing an ExceptionRethrowing an exception in C++ involves catching an exception within a try block and instead of dealing with it locally throwing it again to be caught by an outer catch block. By doing this. we preserve the type and details of the exception ensuring that it can be handled at the appropriate level within our program.
This approach is generally used when handling exception at multiple levels.
Exception SpecificationC++ provides exception specifications to specify that the given function may or may not throw an exception. In modern C++, there are two types of exception specifications:
These exception specifications are used in the function declaration as shown:
C++
void func1(int a) noexcept {
...
}
void func2(int b) noexcept(false) {
...
}
There were other exception specifications, but they have been deprecated since C++ 11.
Why do we need Exception Handling in C++?Errors or abnormal conditions can also be handled without exception handling, like it is done in C using conditional statements. But a exception handling provides the following advantages over traditional error handling:
Exception handling in C++ is a useful tool to make a robust program but there are several common mistakes to avoid when implementing exception handling in our programs. So, there are some best practices that should be followed in exception handling:
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4