Question

The following code demonstrates a weird problem I have in a Turbo C++ Explorer project. One of the three stack objects in D::D() is not destroyed after going out of scope.

This only happens if compiled in release mode, the auto_ptrs a_ and b_ are of different types and the exception thrown doesn't inherit from std::exception. It appears to work just fine in VC++ 2005 and C++ Builder 2009. I did install the BDS2006 Update 2, the hotfix rollup and hotfix 12.

Is it my code or the compiler? Do you know of a fix? Not being able to reliably use auto_ptr in a VCL project would be quite inconvenient.


#include <memory>
#include <stdexcept>
#include <iostream>

typedef std::exception my_error; // will work fine if replaced with line below
//class my_error : public std::exception {};

class A {};
class B {};

class C
{
public:
    C(int id) : id_(id) { std::cout << "C::C() " << id_ << std::endl; };
    ~C() { std::cout << "C::~C() " << id_ << std::endl; };
private:
    int id_;
};

class D
{
public:
    D()
    {
        C c1(1);
        C c2(2);
        C c3(3);

        throw my_error();
    };

private:
    std::auto_ptr<A> a_;
    std::auto_ptr<B> b_; // will work fine if replaced with line below
//  std::auto_ptr<A> b_;
//  std::auto_ptr<C> c_; // see expected output
};

#pragma argsused
int main(int argc, char* argv[])
{
    try
    {
        D d;
    }
    catch (...)
    {
        std::cout << "caught exception" << std::endl;
    }

    return 0;
}


Expected:

C::C() 1
C::C() 2
C::C() 3
C::~C() 3
C::~C() 2
C::~C() 1
caught exception


Got:

C::C() 1
C::C() 2
C::C() 3
C::~C() 2
C::~C() 1
caught exception


Got (with line '// std::auto_ptr<C> c_;' uncommented):

C::C() 1
C::C() 2
C::C() 3
C::~C() 1
caught exception


Edit: Made suggested changes

Edit 2:
I just tested it with C++ Builder 2007 (11.0.2902.10471), which shows the same problem. The release configuration works as soon as I check the "Debug information" box in Project -> Options -> C++ Compiler -> Debugging. It surprises me that the executable gets smaller with "Debug information" enabled (down to 31.5 KB from 39.5 KB ).

Edit 3:
In Turbo C++ Explorer (C++ Builder 2006) (10.0.2288.42451) the release configuration works if I uncheck the "Inline function expansion (-vi)" box in Project -> Options -> C++ Compiler -> Debugging. Replacing the first line (#include <memory>) with the following code makes it work, too.

#pragma option push -vi-
#include <memory>
#pragma option pop 
Was it helpful?

Solution

This appears to be a compiler bug. I just ran the same sample in VS2008SP1 and got the expected output.

OTHER TIPS

For whatever it's worth, GCC 3.4.6 does the expected thing:

$ g++ main.cpp

$ a.out
C::C()
C::C()
C::~C()
C::~C()
caught exception

It's a compiler bug in C++Builder 2006. C++Builder 2009 fixes it; this is the output I get for BCC v6.1:

C::C() 1
C::C() 2
C::C() 3
C::~C() 3
C::~C() 2
C::~C() 1
caught exception

If an exception is thrown in an object constructor, the destructor will not run.

The compiler has no way of knowing if the constructor completed sufficiently for the destructor to run correctly.

See http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.4

EDIT: Responding to the comment below... In this case, it's most likely a compiler bug lumping the 'don't run the destructor' rule in with incorrectly not destroying objects on the stack.

Maybe the cout stream is not flushed? Can you try with cerr instead? or directly put a breakpoint in destructor and check if they are hit?

I just tested this on the free commandline bcc5.5.1 and C++ Builder 6 bcc5.64 and both of them work as expected -- which is a surprise considering how old they are. Then I tried this in C++ Builder 2007, bcc5.93 and the bug is present there. In fact, the example code could be simplified to primitive types and the bug would still be present:

class D
{
public:
    D();

private:
    std::auto_ptr<int>      a_;
    std::auto_ptr<short>    b_;
    std::auto_ptr<char>     c_;
    std::auto_ptr<bool>     d_;
};

This extreme example ends up causing none of the corresponding destructor's to be called for class C! If you're interested in diagnosing this bug further, one trick you can perform is to insert an assembly breakpoint inside your D::D() ctor:

// Note that D::D() ctor can't be inlined if it contains assembly
// limitation of borland compilers unfortunately
D::D()
{
    __asm int 3;
    C c1(1);
    C c2(2);
    C c3(3);

    throw my_error();
}

You would then let it run through the debugger. When execution reaches that specified breakpoint the program will stop and control will be transfer back to the debugger. You can then single step through the assembly to see where the problem lies.

Looks like a bug in the exception handling stack unwind code. Try making a simple class E with an instance of it in D's constructor and see if it gets called.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top