Pavle Šarenac
Pavle Šarenac

Reputation: 103

Exception handler catch(...) in C++

I understand that exception handler catch(...) in C++ handles any type of exception. I am wondering what happens if it has to handle an exception that is of some class type - is the object of that class passed to this handler by reference or by value?

This is the code that could help in understanding this. When the exception of type B is thrown in "Inner try", an unnamed temporary object of class B is created with the default constructor of class B. When I run this program, copy constructor of class B doesn't get called at all, and this unnamed temporary object of class B is destroyed only after executing "Outer try 3", which means that object was propagated backwards all the way to the "Outer try 3". So my guess is that objects are passed by reference to the catch(...) handler, but I wanted to see if this is correct reasoning.

    #include <iostream>
    
    using namespace std;
    
    class A {
    public:
        A() {
            cout << "Default constructor of class A" << endl;
        }
        A(const A& a) {
            cout << "Copy constructor of class A" << endl;
        }
        A(A&& a) noexcept {
            cout << "Move constructor of class A" << endl;
        }
        ~A() {
            cout << "Destructor of class A" << endl;
        }
    };
    
    class B {
    public:
        B() {
            cout << "Default constructor of class B" << endl;
        }
        B(const B& b) {
            cout << "Copy constructor of class B" << endl;
        }
        B(B&& b) noexcept {
            cout << "Move constructor of class B" << endl;
        }
        ~B() {
            cout << "Destructor of class B" << endl;
        }
    };
    
    int main() {
        try {
            try {
                try {
                    try {
                        throw A();
                    }
                    catch (A a) {
                        cout << "Inner try" << endl;
                        throw B();
                    }
                }
                catch (...) {
                    cout << "Outer try 1" << endl;
                    throw;
                }
            }
            catch (...) {
                cout << "Outer try 2" << endl;
                throw;
            }
        }
        catch (...) {
            cout << "Outer try 3" << endl;
        }
    }

Upvotes: 3

Views: 442

Answers (1)

user17732522
user17732522

Reputation: 76628

When you throw an exception, the exception object is created from the operand to throw. It is an object stored in an unspecified way.

When a matching handler for the exception is reached and the handler is not ..., then the handler's parameter is initialized from the exception object.

Essentially, if you declare a catch parameter as a non-reference type, you are getting a copy of the exception object (or of a base class subobject) in the handler. This is what you see in the A handler and why there is a call to the copy constructor. The copy lives until the handler exits. If you declare the parameter a reference type, then you will get a reference to the exception object and it will always refer to the same object if you rethrow the exception.

Rethrowing with throw; does not actually do anything with the parameter of the catch clause. It simply causes the exception handling to continue searching the catch clauses and stack unwinding. The exception object is still the same.

Nothing about this changes if the handler is catch(...). There is no variable (reference or not) to initialize in the handler, but throw; will rethrow and continue the search for a handler with the same exception object.

When a handler exits without rethrowing the exception, then the exception object is destroyed. This happens at the end of your outer-most catch for the B exception object and at the end of the inner-most catch for the A exception object. (You get two destructor calls to A, one for the catch parameter and one for the exception object.)

Upvotes: 3

Related Questions