Exception Handling in C++

Last Updated : 3 Jul, 2026

Exception Handling in C++ is a mechanism used to handle runtime errors and abnormal conditions, allowing a program to continue execution smoothly even in the presence of errors.

  • Handles abnormal conditions that occur during program execution.
  • Helps maintain program stability by preventing unexpected program termination.

Basic try-catch Example

The try block contains code that might throw an exception, while the catch block handles the exception if it occurs.

C++
#include <iostream> 
using namespace std; 
int main() 
{ 
    int n = 10; 
    int m = 0; 
    
    try { 
        if (m == 0) 
        throw "Division by zero"; 
        cout << "Answer: " << n / m; 
        
    } 
    catch (const char* msg) {
        cout << "Error: " << msg; 
        
    } 
    return 0; 
    
}

Output
Error: Division by zero

Internal Working of try-catch Block

When an exception occurs:

  • The runtime executes code inside the try block.
  • If an exception is thrown, the remaining code inside the try block is skipped.
  • The runtime searches for a matching catch block.
  • If found, the exception is handled.
  • If no matching handler is found, terminate() is called.
  • During this process, stack unwinding occurs and local objects are destroyed automatically.

Note: If an exception is not handled, the program terminates abruptly.

throw Keyword

The throw keyword is used to explicitly throw an exception.

C++
#include <iostream> 
using namespace std; 
void checkAge(int age) { 
    
    if (age < 18) 
        throw "Age must be 18 or above"; 
    
} 

int main() {
    
    try { 
        checkAge(15); 
        
    } 
    catch (const char* msg) { 
        cout << msg; 
        
    } 
    return 0; 
    
}

Output
Age must be 18 or above

C++ Exception Hierarchy

Most standard exceptions in C++ derive from the std::exception class.

cpp-exception-hierarchy

Types of Exceptions

There are mainly three types of exceptions in C++:

Built-in Exceptions

Built-in exceptions involve throwing primitive data types such as int, char, or float. Although simple, built-in exceptions provide limited information about the error.

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;
}

Output
Exception Caught: -1

Standard Exceptions

C++ provides a hierarchy of standard exception classes defined in <exception> and <stdexcept>.

Some commonly used standard exceptions are:

  • runtime_error
  • logic_error
  • out_of_range
  • invalid_argument
  • overflow_error

All standard exceptions derive from std::exception and provide the what() function.

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;
}

Output
Caught: vector::_M_range_check: __n (which is 10) >= this->size() (which is 3)

Note: We can also manually throw standard exceptions using throw statement.

Custom Exceptions

When standard exceptions are insufficient, custom exception classes can be created.

C++
#include <iostream>
#include <exception>
using namespace std;

// Custom exception class
class NegativeValueException : public exception {
private:
    int value;
public:
    // Constructor
    NegativeValueException(int val) : value(val) {}

    // Override what() method
    const char* what() const noexcept override {
        return "Negative value error occurred!";
    }

    // Optional: method to get the invalid value
    int getValue() const {
        return value;
    }
};

// Function that throws the custom exception
void checkValue(int x) {
    if (x < 0) {
        throw NegativeValueException(x);
    }
    else {
        cout << "Value is: " << x << endl;
    }
}

int main() {
    int numbers[] = {10, -5, 20};

    for (int n : numbers) {
        try {
            checkValue(n);
        }
        catch (NegativeValueException &e) {
            cout << "Exception caught: " << e.what() 
                 << " Value = " << e.getValue() << endl;
        }
    }

    return 0;
}

Output
Value is: 10
Exception caught: Negative value error occurred! Value = -5
Value is: 20

Nested try-catch

In Nested try-catch, one try-catch block can be placed inside another.

C++
#include <iostream>
using namespace std;

int main() {

    try {

        cout << "Outer try block\n";

        try {
            throw 10;
        }
        catch (int e) {
            cout << "Inner catch: " << e << endl;
        }

        throw "Error";

    }
    catch (const char* msg) {
        cout << "Outer catch: " << msg;
    }
}

Output
Outer try block
Inner catch: 10
Outer catch: Error

Handling Multiple Exceptions

Multiple exceptions can be handled using multiple catch blocks.

try {
// code
}

catch (invalid_argument& e) {
// handle invalid argument
}

catch (out_of_range& e) {
// handle out of range
}

catch (...) {
// handle unknown exception
}

The catch(...) block acts as a catch-all handler.

Exception Propagation (Stack Unwinding)

Exception propagation refers to the process by which exceptions travel through the call stack.

C++
#include <iostream>
using namespace std;

class Demo {
public:
    Demo() {
        cout << "Object Created\n";
    }

    ~Demo() {
        cout << "Object Destroyed\n";
    }
};

int main() {

    try {
        Demo d;
        throw 100;
    }
    catch (int) {
        cout << "Exception Caught";
    }
}

Output
Object Created
Object Destroyed
Exception Caught

try_and_catch

Need for Exception Handling

Exception handling provides several advantages:

  • Separates error-handling code from normal program logic.
  • Supports exception propagation across function calls.
  • Improves readability and maintainability.

Best Practices of Exception Handling

Following these best practices helps improve code reliability, maintainability, and ensures that exceptions are handled efficiently and safely.

  • Use exceptions only for exceptional situations.
  • Catch exceptions by const reference.
  • Prefer standard exception classes when possible.
  • Use RAII for resource management.
  • Avoid generic catch(...) unless necessary.
Comment