2

I have the following code:

#include <iostream>
#include <vector>
#include <tr1/memory>

struct FooError {};

struct Foo
{
    ~Foo() { std::cerr << "~Foo() executed" << std::endl; }
    explicit Foo(unsigned int index) { if (5 == index) throw FooError(index); };
};


int main() {
    typedef std::tr1::shared_ptr<Foo> FooPtr;
    std::vector<FooPtr> foos;
    for (unsigned int index = 0; index < 20; ++index)
    {
        try
        {
            foos.push_back(FooPtr(new Foo(index)));
        }
        catch (const FooError&)
        {
            std::cerr << "FooError caught" << std::endl;
        }
    }
}

I see a set of ~Foo() executed when there are try{} catch{} blocks. When there are no exception handlers, nothing is printed. Does it mean that destructors of stack-allocated objects are called when exception is handled? Or is nothing printed because of std::cerr buffering issues?

outis
  • 75,655
  • 22
  • 151
  • 221
bananov
  • 163
  • 1
  • 5

4 Answers4

5

Here are the details of what's going on from the C++03 standard.

  • From 15.3/9 Handling Exceptions

    If no matching handler is found in a program, the function terminate() is called;

  • From 18.6.3 Abnormal termination:

    The implementation’s default terminate_handler calls abort().

  • And from 3.6.3/4 Termination:

    Calling the function void abort(); declared in <cstdlib> terminates the program without executing destructors for objects of automatic or static storage duration and without calling the functions passed to atexit().

So that's why your foos object isn't being destructed (it has static storage duration). However, even if you change it so that it's a local variable (having automatic duration), that might not fix the problem (emphasis added):

So for static duration objects, destructors aren't called unless you change the terminate handler (maybe to have it call exit() instead of abort()). However, for automatic objects, there remains a possible problem (emphasis added):

15.5.1/1 The terminate() function

In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before terminate() is called. In all other situations, the stack shall not be unwound before terminate() is called.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
3

The program's scope unwinding, whether through normal execution or via try/throw/catch, only happens if the application exits by returning from main. If the application exits via an exception (or via abort() or terminate()), no unwinding happens and no destructors get called.

This pertains to both automatic and static objects.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • If we catch an exception say halfway between the point it occured and `main()` return point, but then *rethrow* it, won't the destructors get called only to the point where we caught the exception? – jrok Dec 18 '11 at 17:50
  • 1
    "no unwinding happens" - in general it's implementation-defined whether it happens or not, in this case not. – Steve Jessop Dec 18 '11 at 19:15
  • @SteveJessop: Can you please check this example: http://ideone.com/X7Hv8 ? Does that mean it's implementation defined whether foo's constructor will be called or not? Or will `foo` always be destructed? – jrok Dec 18 '11 at 19:23
  • @jrok: 15.2 in the standard says, "As control passes from a throw-expression to a handler, destructors are invoked for all automatic objects constructed since the try block was entered", so `foo` will be destructed when the handler is entered. The re-throw will not be handled, so it's implementation-defined whether the dtor for `B` will be invoked. – Michael Burr Dec 18 '11 at 20:09
2

The destructors are being called (from the vector) after the loop, right before the program exits.

If you don't catch the exception, terminate is called which aborts the program without calling destructors.

Pubby
  • 51,882
  • 13
  • 139
  • 180
  • 1
    "without calling destructors" - that's what happened on this implementation, but it's not guaranteed that they won't be called (15.3/9 in C++11). – Steve Jessop Dec 18 '11 at 19:15
1

If you catch the exception the deallocators will be called to clean up the memory. If you dont catch the exception the application will just quit.

A vector actually stores all its data on the heap by the way; that is why it is resizable. You can think of the data on the stack as being a pointer to the memory on the heap (which is hidden from you).

bashirs
  • 452
  • 1
  • 7
  • 14
  • 1
    +1. I didn't realize the OP is talking about the situation when there's no try/catch, thought it's about when exception isn't thrown. – Michael Krelin - hacker Dec 18 '11 at 17:22
  • 1
    But what's left of my now deleted answer is still important — the `Foo`s aren't stack-allocated. – Michael Krelin - hacker Dec 18 '11 at 17:23
  • I wonder why no stack unwinding happens. What if have several block levels and an error is caught on the lower's one, will I have memory leaks because FooPtr's aint freed? – bananov Dec 18 '11 at 17:31
  • I have always thought that when exception is raised it goes up to the program and unwinds the stack, on each sub-stack unwind destructors are called for the each stack-allocated object. – bananov Dec 18 '11 at 17:33
  • If you catch the exception the deconstructors will be called. If you dont catch the application will quit and go to the OS. If the application quits the OS will reclaim all the memory so it will not be a memory leak. The issue though is if your deconstructor did something like release a database lock it wont be called. The OS will not call deconstructors (it doesnt know about them) it just reclaims the memory. – bashirs Dec 18 '11 at 18:40