Stack Unwinding In C++

In this article, we will discuss stack unwinding in C++ with several methods and an example.

What is Stack Unwinding in C++?

When an exception is thrown in C++ , a procedure known as stack unwinding takes place. Upon the occurrence of an exception, the C++ runtime system initiates unravelling or unrolling the function call stack to identify an appropriate catch clause capable of managing the exception. This method continues until a suitable catch block is located or if none is discovered until the programme ends.

1. Exceptions Throwing

The throw keyword in C++ is used to cast exceptions. Any type, including user-defined and built-in types and objects, may be an exception.

Errors or unusual circumstances that arise while a programme is being executed are usually the reason for throwing exceptions.

Example

throw SomeExceptionType(arguments);

2. Try-Catch Blocks

C++ offers try-and-catch blocks for handling exceptions. While the catch block describes how to handle particular exception types, the try block contains the Code that might throw an exception.

Example

try
{
    //Code that may throw an exception
} 
catch (const SomeException& ex) 
{
    // Handle SomeException
} 
catch (const AnotherException& ex)
 {
    // Handle AnotherException
}

3. Stack Unwinding Process

The C++ runtime system searches for a suitable catch block to handle an exception that is thrown inside a try block.

The local variables of each Function in the call stack are destroyed as the control climbs the call stack because the function call stack is unwound, also known as unrolled.

As they were constructed, local variable destructors are invoked in the opposite order.

4. RAII and Destructors

The stack unwinding procedure is essential for resource management based on the RAII (Resource Acquisition Is Initialization) principle.

During stack unwinding, destructors of objects with automatic storage duration (local variables) are invoked to ensure appropriate resource cleanup.

5. Termination or std::terminate

The std::terminate Function is invoked, which results in programme termination if any catch blocks do not capture the exception during the stack unwinding procedure.

6. Cleanup and Resource Management

Despite exceptions, stack unwinding offers a way to clean up by releasing memory, shutting files, and releasing other resources.

7. Custom Exception Classes

Users may inherit any other exception type or std::exception to construct their exception classes. It enables better organized and informed handling of exceptions.

Example:

Let us take an example to illustrate the stack unwinding in C++.

Example

#include <iostream>
#include <stdexcept>
// Custom exception base class
class MyBaseException : public std::exception 
{
public:
    const char* what() const noexcept override
    {
        return "MyBaseException occurred!";
    }
};
// Derived exception classes
class MyDerivedException1 : public MyBaseException
{
public:
    const char* what() const noexcept override
    {
        return "MyDerivedException1 occurred!";
    }
};
class MyDerivedException2 : public MyBaseException 
{
public:
    const char* what() const noexcept override 
    {
        return "MyDerivedException2 occurred!";
    }
};
//Function that may throw exceptions
void foo()
{
    throw MyDerivedException1();
}
// Another function that may throw exceptions
void bar()
{
    throw MyDerivedException2();
}
int main()
{
    try
    {
        std::cout << "Entering try block in main.\n";
        foo();
        bar(); // This won't be executed because 'foo' throws an exception
        std::cout << "Exiting try block in main.\n";
    } 
    catch (const MyBaseException& ex)
    {
        std::cerr << "Caught an exception: " << ex.what() << std::endl;
    }
    std::cout << "Program continues after catch block.\n";
    return 0;
}

Output:

Output

Entering try block in main.
Caught an exception: MyDerivedException1 occurred!
The program continues after the catch block.

Explanation:

  1. Include Headers

These are the typical C++ header files. Standard exception classes are stored in <stdexcept>, while input/output operations are stored in <iostream>.

  1. Base Class for Custom Exception

A custom exception class called MyBaseException is derived from std::exception.

It overrides the what Function to provide a unique error message.

  1. Derived Exception Classes

MyBaseException is the parent class of MyDerivedException1 and MyDerivedException2.

Their corresponding error messages take precedence over the what Function.

  1. Functions That May Throw Exceptions

The functions bar and foo have the potential to throw exceptions of the MyDerivedException1 and MyDerivedException2 types, respectively.

  1. Main Function

A try block is located in the main Function.

When foo is invoked, it throws a MyDerivedException1, which prevents the bar from being run.

A catch block catches exceptions of the type MyBaseException or its derived classes.

The catch block uses ex.what to print details about the caught exception.

Conclusion:

In conclusion, this Code illustrates utilising the custom exception classes (MyBaseException, MyDerivedException1, and MyDerivedException2) within a try-catch chunk. Polymorphic behaviour during stack unwinding is demonstrated by demonstrating how the catch block can catch exceptions of a base class type. The error message corresponding to the captured exception is printed in the catch block. The catch block signals that the stack unwinding operation has finished, and the program proceeds.

Input Required

This code uses input(). Please provide values below: