0
votes

I'm trying to understand why the destructors throwing exceptions is a very bad idea. By googling, I understand that if destructor throws, say, during destruction of the block-scope objects, the destruction of the obeject stopped and we get memory leak if there're the other objects destructors have not called for.

But by itself, the destructor throwing exceptions is so bad? For instance (the complete example):

struct A
{
    int a;
    char b;
    A() : a(10), b('a') { }
    ~A(){ throw std::exception(); }
};

int main()
{
    A a; //destructor throws, but there're no more objects to destruction 
         //therefore I think there's no memory leak
}

Do we get UB in the program above?

1
In this program there isn't a memory leak, since the program exits immediately due to the fact that a thrown exception wasn't caught. The struct A does have a problem, if it ends up being used in any non-trivial manner. I'm not quite sure about the Undefined Behavior question though, I'm not a language-lawyer for C++. - Daniel
And what exactly do you plan to do with this exception?? - Swastik Padhi
@Daniel I know that we don't ever do such things in real code, but try to undestand the UB though. - St.Antario
@CRAKC Nothing, it's just an example... - St.Antario
If your main has a try/catch block, there wouldn't be any UB and all's good, but you'd have trouble taking A and using it in a more complicated program. There are lots of existing Q&A about the issues, e.g. here. - Tony Delroy

1 Answers

5
votes

In this trivial example, the exception in bar() is thrown and the stack is unwound, destructing a, which throws another exception. Which exception could be thrown in this case?

struct A
{
    int a;
    char b;
    A() : a(10), b('a') { }
    ~A(){ throw std::exception(); }
};
void bar () 
{
    A a;
    throw std::exception();
}
int main()
{
    bar();
}

From C++ FAQ

During stack unwinding, all the local objects in all those stack frames are destructed. If one of those destructors throws an exception (say it throws a Bar object), the C++ runtime system is in a no-win situation: should it ignore the Bar and end up in the } catch (Foo e) { where it was originally headed? Should it ignore the Foo and look for a } catch (Bar e) { handler? There is no good answer — either choice loses information.

So the C++ language guarantees that it will call terminate() at this point, and terminate() kills the process.