2

What happens when we throw from a destructor? I know that it causes terminate() to be called, and memory is indeed freed and the destructor is called, but, is this before or after throw is called from foo? Perhaps the issue here is that throw is used while the stack is unwinding that is the problem.

Nick Bolton
  • 38,276
  • 70
  • 174
  • 242
  • 5
    Like many Brainbench questions, this comes into the category of "what happens if you do something you should never do?", and as such is not really worth the trouble answering. –  Mar 13 '10 at 12:50
  • 2
    @Neil, sometimes such answers are nevertheless useful. If you know that `S` happens if you do `X`, then if you see `S` while debugging you have pointers that somewhere you (or someone else) have done `X`. – P Shved Mar 13 '10 at 12:52
  • 4
    @Neil: yes, throwing a `const char*`. That's shocking ;-) I think the question is fine, I'd rather have a programmer who knows *why* not to throw from a destructor, because he knows the consequences, than a programmer who has been ordered by an authority he trusts not to do it, never does it, but doesn't know or care why he's following this rule. Having the choice between the two at all is a good start, mind you. – Steve Jessop Mar 13 '10 at 13:11
  • 1
    @Steve My point was that given the code, the headline question is not really answerable. Is the memory freed? Yes, I suppose so - if terminate is called, and the app exits all memory is freed, but so what? –  Mar 13 '10 at 13:13
  • You're right, if the brainbench question uses the same wording as the title of this question then it's unanswerable. Whether there is a "stack" and what happens to its "memory" on application termination is outside the scope of the C++ standard. But what happens before that is defined. Personally, I'd have put an automatic variable in the try block in `main`, and asked whether its destructor is called. "I don't remember but I know where to look it up" would probably be an acceptable answer at interview :-) – Steve Jessop Mar 13 '10 at 13:17
  • You're both right: I feel the title is inappropriate for the question as a whole and I should have thought about it a little more before posting. – Nick Bolton Mar 13 '10 at 14:47
  • I have updated the title; hopefully this makes more sense. – Nick Bolton Mar 13 '10 at 14:50
  • 1
    @nbolton: I think the term you might be looking for is "stack unwinding". Perhaps a better title would be: "Are destructors of automatic objects invoked when terminate is called" or something like that. Basically I think, there's no more memory management for the stack than adjusting a pointer to it when you enter and leave functions (which is more or less set up by the compiler while compiling). – UncleBens Mar 13 '10 at 14:57
  • @UncleBens Renamed; let's see what others think. – Nick Bolton Mar 13 '10 at 15:39
  • Can yoi include your example code in the question. External links are not really convenient. –  Jun 28 '18 at 10:22
  • Argh, looks like the image was taken down. I can't remember what it was. This was a poorly written SO question anyway. – Nick Bolton Jul 25 '18 at 06:30
  • p.s. Reworded so it makes sense without the screenshot. – Nick Bolton Jul 25 '18 at 06:32

6 Answers6

13

Is this before or after throw is called from foo?

This is what is happening:

  • foo() is called
  • An object a of type A is created on the stack
  • The next statement throws
  • Now, the dtor for a is called, which throws another exception
  • std::terminate is called -- which is nothing but abandoning the exception handling mechanism:

From C++0x draft:

15.5.1 The std::terminate() function

1 In the following situations exception handling must be abandoned for less subtle error handling techniques:

[...] — when the destruction of an object during stack unwinding (15.2) exits using an exception, or

2 In such cases, std::terminate() is called (18.7.3). In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before std::terminate() is called. In all other situations, the stack shall not be unwound before std::terminate() is called. An implementation is not permitted to finish stack unwinding prematurely based on a determination that the unwind process will eventually cause a call to std::terminate().

Note: Emphasis mine

Community
  • 1
  • 1
dirkgently
  • 108,024
  • 16
  • 131
  • 187
2

Here's what happens in g++:

#include <stdio.h>
class A {
public:
    ~A()
    {
        fprintf(stderr, "in ~A\n");
        throw "error";
    }
};

void foo()
{
    A a;
    fprintf(stderr, "in foo\n");
    throw "error";
}

int main()
{
    try {
        foo();
    }
    catch (const char*) {
        return 1;
    }
    return 0;
}


[~/ecc/ellcc/ecc] main% ./a.out
in foo
in ~A
terminate called after throwing an instance of 'char const*'
Abort
[~/ecc/ellcc/ecc] main% 

As you can see, the throw in foo happens first, then the throw in ~A causes the error.

Richard Pennington
  • 19,673
  • 4
  • 43
  • 72
  • @Richard Thanks, I updated my answer for MS C++ -- seems very similar; I thought the behaviour was undefined between platforms, but apparently not. – Nick Bolton Mar 13 '10 at 13:03
1

If I'm not mistaken, once terminate is called, no (further) stack unwinding would occur.

terminate calls a handler function (which you can set with set_terminate):

The type of a handler function to be called by terminate() when terminating exception processing.
Required behavior: A terminate_handler shall terminate execution of the program without returning to the caller.
Default behavior: The implementation's default terminate_handler calls abort().

At least I don't know of a way to "terminate execution without returning to the caller" that would allow you to unwind the stack.

You can modify the example to see what you can expect:

#include <cstdio>

class A
{
    public:
        ~A() {
            puts("Entered A destructor");
            throw "error";
        }
};

void foo()
{
    A a, b;
    throw "error";
}

int main()
{
    try {
        foo();
    } catch (const char*) {
        return 1;
    }
}

Now there are two A instances, and the destructor of the second one is never called, because the execution was terminated as soon as the destructor of the first A finished and let another exception escape.

UncleBens
  • 40,819
  • 6
  • 57
  • 90
1

You got is slightly wrong and that's why you don't understand it. You see, throw in destructor is not causing teriminate() function to be called, it is a bad practice, but it is not fatal for program execution. What is fatal is that some code throws while there's still active exception. C++ can't decide what exception to propagate further, new one or old one and it can't propagate them both. It is considered fatal for program execution and that's why terminate is called.

So, you see, without throw in foo, terminate wouldn't be called but there will be an exception thrown from ~A. So, naturally, throw in foo has to be called first and then during the second throw everything breaks.

vava
  • 24,851
  • 11
  • 64
  • 79
0

Object a is a stack object, so there is no dynamic memory to be freed. Once control goes out of the scope of foo(), the stack frame, and therefore the object, no longer exists.

Tim Martin
  • 3,618
  • 6
  • 32
  • 43
  • Actually, I believe you could say that memory on the stack is still freed once out of scope even if it's not dynamic. However, that's not the question here, I'm also asking about use of `throw` in a destructor. – Nick Bolton Mar 13 '10 at 12:47
0

To illustrate, here's what happens in Microsoft C++:

#include <iostream>

class A {
public:
    ~A() {
        std::cout << "in ~A" << std::endl;
        throw "error";
    }
};

void foo() {
    A a;
    std::cout << "in foo" << std::endl;
    throw "error";
}

int main() {

    try {
        foo();
    }
    catch (void *) {
        std::cout << "exit: 1" << std::endl;
        return 1;
    }
    std::cout << "exit: 0" << std::endl;
    return 0;
}

And the result:

>cpptest1.exe
in foo
in ~A

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

>
Nick Bolton
  • 38,276
  • 70
  • 174
  • 242
  • 1
    Hehe. "contact the application's support team". I think g++'s error is a little more informative, especially if you don't have a support team. – Richard Pennington Mar 13 '10 at 13:11
  • @Richard Pennington +1 Haha, yeah; the g++ message is *maybe* less user friendly, but certainly way more programmer friendly. – Nick Bolton Mar 13 '10 at 14:51