hshsvagen
hshsvagen

Reputation: 33

Throwing an exception when another exception has not been handled yet. (C++)

As far as I know, throwing an exception when another has not been handled yet is undefined behavior, and the program may crash. An exception is considered unhandled until it gets into the catch-block. I have the following code snippet:

#include <iostream>

using namespace std;

class ThrowingInDestructorClass
{
    public:
    ThrowingInDestructorClass() = default;
    
    ~ThrowingInDestructorClass() { 
        try {
            throw std::runtime_error("2-nd exception. ");
        }
        catch(...)
        {
            std::cout << "Catched an error during Throwing class cleanup. " << std::endl;
        };
        
    }
};

void doSmth()
{
    try {
        ThrowingInDestructorClass throwing;
        
        throw std::runtime_error("1-St exception. ");
    }
    catch(...)
    {
        std::cout << "Catched an error during doSmth. " << std::endl;
    };
}

int main() {    
    doSmth();
}

Here we have a class that can throw and handle an exception inside its destructor, so it is OK. But we have a method that creates objects and throws an exception. During stack-unwinding, the class destructor will be called, throwing a 2-nd exception. So the 1-St exception will be unhandled yet. When I run it, I get the following output:

Caught an error during Throwing class cleanup. 
Caught an error during doSmth. 

It may seem that everything is fine, but I'm not entirely sure that there is no UB here. Could someone help to clarify the situation?

Upvotes: 0

Views: 129

Answers (1)

Jan_Lukas
Jan_Lukas

Reputation: 38

This answer is mostly just a summary of the comments: I'll go through the program step by step starting at the point the first exception is thrown:

  1. Stack unwinding begins by destroying the "throwing" object.
  2. The destructor of the "throwing" object gets called, throwing another exception.
  3. Stack unwinding finds a catch block for the new exception immediately, handling the exception. The destructor exits normally.
  4. The stack unwinding finds the catch block for the first exception, handling it.
  5. doSmth() exits normally

No function exited with an exception, thereby the condition

"...If any function that is called directly by the stack unwinding mechanism, after initialization of the exception object and before the start of the exception handler, exits with an exception, std::terminate is called. ..." en.cppreference.com/w/cpp/language/throw - Richard Critten

isn't met.

And even if you remove the try catch in the destructor, no UB will occur:

#include <iostream>

using namespace std;

class ThrowingInDestructorClass
{
    public:
    ThrowingInDestructorClass() = default;

    ~ThrowingInDestructorClass() { 
        throw std::runtime_error("2-nd exception. ");
    }
};

void doSmth()
{
    try {
        ThrowingInDestructorClass throwing;
    
        throw std::runtime_error("1-St exception. ");
    }
    catch(...)
    {
        std::cout << "Catched an error during doSmth. " << std::endl;
    };
}

int main() {    
    doSmth();
}

This program will crash because std::terminate is called when stack unwinding (as now a function exits with an exception).

If you throw an exception when another has not been handled yet, and you let the second exception escape a destructor that was called in process of handling the first exception, then you get a well-defined program termination. – n. 1.8e9-where's-my-share m.

Your program will crash, but that crash is not UB but well-defined behaviour

Upvotes: 1

Related Questions