3

I have the following code:

#include <iostream>
using namespace std;

class A {
public:
    A()  { cout << "A::A()" << endl;}
    ~A() { cout << "A::~A()" << endl; throw "A::exception";}
};

class B {
public:
    B()  { cout << "B::B()" << endl; throw "B::exception";}
    ~B() { cout << "B::~B()";}
};

int main() {
    try {
        cout << "Entering try...catch block" << endl;
        A   objectA;
        B   objectB;
        cout << "Exiting try...catch block" << endl;
    } catch (char const * ex) {
        cout << ex << endl;
    }
    return 0;
}

Now, before stating the question, I would like to point that this code is bad practice (e.g throwing an exception from a constructor will result the object not being fully created, thus the destructor won't be called and it might cause memory leaks or other problems).

Now, The order of the main is this:

  1. Printing "Entering try...catch block".

  2. Calling A's constructor, printing "A::A()"

  3. Calling B's constructor, printing "B::B()", and throws exception.

  4. The exception was thrown, and the line "Exiting try...catch block" will not be printed. The block is exited, so A's destructor is called.

  5. A's destructor prints "A::~A()" and throws another exception.

The second exception (in 5) causes the main to throw an exception, before entering the catch block.

My question is this - is there a way to catch the second exception in the main, without altering the classes A,B?

I have tried to surround both the whole try-catch block and inside the catch block with another try-catch block but that didn't work.

Thanks.

Mickey
  • 1,405
  • 2
  • 13
  • 33
  • 7
    throwing in destructor during stack unwinding causes a program termination, it is a very bad habit to throw in destructors – Tyker Jun 20 '18 at 10:19
  • There can be only one active exception at any time. – Yksisarvinen Jun 20 '18 at 10:35
  • 3
    minor note: throwing an exception in a constructor is - in general - absolutely fine. – Mike Vine Jun 20 '18 at 10:37
  • 3
    BTW. Throwing from constructor is not bad, it's pretty much the only way to signal something went wrong in construction. But if you work with raw pointers, then you should take care about them. – Yksisarvinen Jun 20 '18 at 10:39
  • 1
    Just an an exercise, you *could* use an inner `try {B objectB;} catch(...){}` and catch that exception separately. Moderately useful though. – Bo Persson Jun 20 '18 at 10:47

2 Answers2

5

From cppreference.com:

As any other function, a destructor may terminate by throwing an exception [...] however if this destructor happens to be called during stack unwinding, std::terminate is called instead.

So, the attempt to throw an exception from ~A() does not result in a second exception being thrown; it results in the program being terminated. If you need to "catch" this "second exception", you would need to interfere with the termination handler. Or you could find a way to not throw an exception in the destructor. Continuing from cppreference.com:

Although std::uncaught_exception may sometimes be used to detect stack unwinding in progress, it is generally considered bad practice to allow any destructor to terminate by throwing an exception.

JaMiT
  • 14,422
  • 4
  • 15
  • 31
0

You may use set_terminate+longjmp in order to avoid program termination.

#include <iostream>
#include <setjmp.h>


using namespace std;

jmp_buf jmpBuf;

class A {
public:
    A()  { cout << "A::A()" << endl;}
    ~A() noexcept(false){ cout << "A::~A()" << endl; throw "A::exception";}
};

class B {
public:
    B()  { cout << "B::B()" << endl; throw "B::exception";}
    ~B() { cout << "B::~B()";}
};

int main() {

    set_terminate([](){
        cout<<"long jump begin" << endl;
        longjmp(jmpBuf,1);
    });

    if(setjmp(jmpBuf)==0) {

        try {
            cout << "Entering try...catch block" << endl;
            A objectA;
            B objectB;
            cout << "Exiting try...catch block" << endl;
        } catch (char const *ex) {
            cout << ex << endl;
        }
    }else{
        cout<<"long jump end" << endl;
    }
    return 0;
}