Exception Handling in C++
Last Updated :
15 May, 2025
In C++, exceptions are unexpected problems or errors that occur while a program is running. For example, in a program that divides two numbers, dividing a number by 0 is an exception as it may lead to undefined errors.
The process of dealing with exceptions is known as exception handling. It allows programmers to make the program ready for any errors that may happen during execution and handle them gracefully so that it keeps running without errors.
try-catch Block
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 Exceptions
Throwing 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:
- Built-in Types
- Standard Exceptions
- Custom Exceptions
Throwing Built-in Types
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;
}
OutputException 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 Exceptions
Standard 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;
}
OutputCaught: 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 Exceptions
When 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 Exceptions
As 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 Exceptions
There 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: 10
Caught an unknown exception.
Catch by Value or Reference
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;
}
OutputCaught: 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;
}
OutputCaught: 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 Exceptions
If 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 Propagation
Exception propagation tells us how the exception travels in the call stack.
- When an exception is thrown, execution of the current block is immediately terminated and all the resources allocated are automatically deallocated (except dynamic resources allocated using new).
- The stack unwinding occurs as the thrown exception propagates through the call stack to look for the matching catch block.
- When the corresponding catch block is found, exception is caught and handled. If not caught, the program terminates
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;
}
OutputInside try block
Object Created
Object Destroyed
Exception Caught
After catch
In the above example,
- When try block runs, and throws 10, then code after the exception is not executed (as "after throw" is not printed). The object gfg is destroyed (as "Object Destroyed" is printed) and the matching catch block is searched for.
- As the matching catch block is found with integer exception, the code inside the block is executed (as "Exception Caught" is printed). Then the execution of the code after the catch block is continued (as "after catch" is printed).
Nested Try Catch Blocks
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 Exception
Rethrowing 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 Specification
C++ 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:
- noexcept: Tells that the given function is guaranteed to not throw an exception.
- noexcept(false): Tells that the given function may throw an exception. It is default behaviour in case no exception specification is used.
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:
- 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 and 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.
Best Practices of Exception 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:
- Don't use exceptions for regular control flow. Only use them for exceptional situations (errors).
- Catch exceptions by reference whenever possible to avoid copying overhead and enable polymorphic selection.
- Always catch exceptions by reference to a constant to avoid accidental modification.
- Catching generic exceptions using catch(...) does not provide information about exception type, so better to catch specific exceptions to handle errors appropriately.
- Failing to catch exceptions can cause the program to terminate unexpectedly. Always ensure exceptions are caught and handled properly to maintain stability.
- Not releasing resources (e.g., memory, file handles) after an exception can led to resource leaks. Use proper cleanup mechanisms such as RAII or finally blocks.
Similar Reads
C++ Tutorial | Learn C++ Programming C++ is a popular programming language that was developed as an extension of the C programming language to include OOPs programming paradigm. Since then, it has become foundation of many modern technologies like game engines, web browsers, operating systems, financial systems, etc.Features of C++Why
5 min read
Introduction to c++
Difference between C and C++C++ is often viewed as a superset of C. C++ is also known as a "C with class" This was very nearly true when C++ was originally created, but the two languages have evolved over time with C picking up a number of features that either weren't found in the contemporary version of C++ or still haven't m
3 min read
Setting up C++ Development EnvironmentC++ is a general-purpose programming language and is widely used nowadays for competitive programming. It has imperative, object-oriented, and generic programming features. C++ runs on lots of platforms like Windows, Linux, Unix, Mac, etc. Before we start programming with C++. We will need an enviro
8 min read
Header Files in C++C++ offers its users a variety of functions, one of which is included in header files. In C++, all the header files may or may not end with the ".h" extension unlike in C, Where all the header files must necessarily end with the ".h" extension. Header files in C++ are basically used to declare an in
6 min read
Namespace in C++Name conflicts in C++ happen when different parts of a program use the same name for variables, functions, or classes, causing confusion for the compiler. To avoid this, C++ introduce namespace.Namespace is a feature that provides a way to group related identifiers such as variables, functions, and
6 min read
Writing First C++ Program - Hello World ExampleThe "Hello World" program is the first step towards learning any programming language and is also one of the most straightforward programs you will learn. It is the basic program that demonstrates the working of the coding process. All you have to do is display the message "Hello World" on the outpu
4 min read
Basics
C++ Data TypesData types specify the type of data that a variable can store. Whenever a variable is defined in C++, the compiler allocates some memory for that variable based on the data type with which it is declared as every data type requires a different amount of memory.C++ supports a wide variety of data typ
7 min read
C++ VariablesIn C++, variable is a name given to a memory location. It is the basic unit of storage in a program. The value stored in a variable can be accessed or changed during program execution.Creating a VariableCreating a variable and giving it a name is called variable definition (sometimes called variable
4 min read
Operators in C++C++ operators are the symbols that operate on values to perform specific mathematical or logical computations on given values. They are the foundation of any programming language.Example:C++#include <iostream> using namespace std; int main() { int a = 10 + 20; cout << a; return 0; }Outpu
9 min read
Basic Input / Output in C++In C++, input and output are performed in the form of a sequence of bytes or more commonly known as streams.Input Stream: If the direction of flow of bytes is from the device (for example, Keyboard) to the main memory then this process is called input.Output Stream: If the direction of flow of bytes
5 min read
Control flow statements in ProgrammingControl flow refers to the order in which statements within a program execute. While programs typically follow a sequential flow from top to bottom, there are scenarios where we need more flexibility. This article provides a clear understanding about everything you need to know about Control Flow St
15+ min read
C++ LoopsIn C++ programming, sometimes there is a need to perform some operation more than once or (say) n number of times. For example, suppose we want to print "Hello World" 5 times. Manually, we have to write cout for the C++ statement 5 times as shown.C++#include <iostream> using namespace std; int
7 min read
Functions in C++A function is a building block of C++ programs that contains a set of statements which are executed when the functions is called. It can take some input data, performs the given task, and return some result. A function can be called from anywhere in the program and any number of times increasing the
9 min read
C++ ArraysIn C++, an array is a derived data type that is used to store multiple values of similar data types in a contiguous memory location.Arrays in C++Create an ArrayIn C++, we can create/declare an array by simply specifying the data type first and then the name of the array with its size inside [] squar
10 min read
Strings in C++In C++, strings are sequences of characters that are used to store words and text. They are also used to store data, such as numbers and other types of information in the form of text. Strings are provided by <string> header file in the form of std::string class.Creating a StringBefore using s
5 min read
Core Concepts
Pointers and References in C++In C++ pointers and references both are mechanisms used to deal with memory, memory address, and data in a program. Pointers are used to store the memory address of another variable whereas references are used to create an alias for an already existing variable. Pointers in C++ Pointers in C++ are a
5 min read
new and delete Operators in C++ For Dynamic MemoryIn C++, when a variable is declared, the compiler automatically reserves memory for it based on its data type. This memory is allocated in the program's stack memory at compilation of the program. Once allocated, it cannot be deleted or changed in size. However, C++ offers manual low-level memory ma
6 min read
Templates in C++C++ template is a powerful tool that allows you to write a generic code that can work with any data type. The idea is to simply pass the data type as a parameter so that we don't need to write the same code for different data types.For example, same sorting algorithm can work for different type, so
9 min read
Structures, Unions and Enumerations in C++Structures, unions and enumerations (enums) are 3 user defined data types in C++. User defined data types allow us to create a data type specifically tailored for a particular purpose. It is generally created from the built-in or derived data types. Let's take a look at each of them one by one.Struc
3 min read
Exception Handling in C++In C++, exceptions are unexpected problems or errors that occur while a program is running. For example, in a program that divides two numbers, dividing a number by 0 is an exception as it may lead to undefined errors.The process of dealing with exceptions is known as exception handling. It allows p
11 min read
File Handling through C++ ClassesIn C++, programs run in the computerâs RAM (Random Access Memory), in which the data used by a program only exists while the program is running. Once the program terminates, all the data is automatically deleted. File handling allows us to manipulate files in the secondary memory of the computer (li
8 min read
Multithreading in C++Multithreading is a technique where a program is divided into smaller units of execution called threads. Each thread runs independently but shares resources like memory, allowing tasks to be performed simultaneously. This helps improve performance by utilizing multiple CPU cores efficiently. Multith
5 min read
C++ OOPS
Standard Template Library (STL)
Practice Problem