A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://www.geeksforgeeks.org/cpp/exception-handling-c/ below:

Exception Handling in C++ - GeeksforGeeks

In programming, mistakes that cause unusual conditions (errors) are common. These errors generally fall into three categories:

What is Exception Handling?

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 Exception

When an error or unexpected situation occurs, the program uses the throw keyword to signal an exception.

2. Catching an Exception

The program searches for a matching catch block to intercept and handle the thrown exception.

3. Handling the Exception

The catch block contains the logic to respond to the error, allowing the program to recover or terminate gracefully.

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:

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

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

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

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

Output
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 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.

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

Output
Inside try block
Object Created
Object Destroyed
Exception Caught
After catch

In the above example,

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:

  1. noexcept: Tells that the given function is guaranteed to not throw an exception.
  2. 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:

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:



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