destructor not called in case of exception with scoped_ptr
Asked Answered
F

2

6

I have just started using c++ boost libraries. I read in many places that when a scoped_ptr is used then the object is always destroyed even in case of exceptions.

They behave much like built-in C++ pointers except that they automatically delete the object pointed to at the appropriate time. Smart pointers are particularly useful in the face of exceptions as they ensure proper destruction of dynamically allocated objects.

I tried in the following code.

#include<boost/scoped_ptr.hpp>

class B
{
  public:
    B(){ std::cout<< "B constructor call\n"; }
    ~B(){ std::cout<<"B destructor call\n"; }
};

class A
{
  public:
  boost::scoped_ptr<B> b;
  A():b(new B())  
  {
    throw 1;
  }
};

int main()
{
    A a; return 0;
}

output:
B constructor call
terminate called after throwing an instance of 'int'
Aborted (core dumped)

There is no call to B's destructor. But I used scoped_ptr so it should have called B's destructor or did I mis-interpreted the use of scoped_ptr.

But if a surround it with try catch then B's destructor is called.

try{
  A a;
} catch( ... ) {
}

In this case destructor of A will be called as all locally allocated objects in case of exception in a try block are removed from the stack and I have my pointer wrapped inside and object of scoped_ptr so when the destructor of scoped object destroys which ultimately the pointer. So is scoped_ptr is useful because we don't have to explicitly delete the memory allocated or I mis-interpreted the description of scoped_ptr.

How can I call the destructor of class B in case of exception using scoped_ptr

Fortin answered 14/10, 2012 at 23:17 Comment(0)
D
15

There's no matching exception handler, so std::terminate is called directly, in this case without the stack being unwound. Put a try/catch in main that catches int and you'll see your destructor call, even if that handler rethrows.

C++11 §15.1/2:

When an exception is thrown, control is transferred to the nearest handler with a matching type; "nearest" means the handler for which the compound-statement or ctor-initializer following the try keyword was most recently entered by the thread of control and not yet exited.

and §15.3/9:

If no matching handler is found, the function std::terminate() is called; whether or not the stack is unwound before this call to std::terminate() is implementation-defined.

Online demo

Debatable answered 14/10, 2012 at 23:27 Comment(5)
If you try to output in your catch block, you'll even see that your destructor is called before the catch block is executed.Primaveria
I was editing the question when you answered the question. And added that thing in question. I forgot to mention it previouslyFortin
@mayurharne : If this doesn't answer your question, then I don't know what your question is. :-] When throwing an exception that is never caught, there is no guarantee of stack unwinding, i.e. there is no guarantee of any live objects being destroyed before process termination. If you want to force stack unwinding in the face of uncaught exceptions, then simply catch and rethrow all exceptions inside of main so that there is always a matching handler on the main thread: int main() { try { /* all real code */ } catch (...) { throw; } }.Debatable
Other then catching the exception is there no other way to implicitly destroy scoped_ptr.Fortin
@mayurharne : No – unless the stack is unwound, there will be no destructor calls for any objects, and the only portable way to force stack unwinding is for there to always be some matching exception handler. (There may be non-portable, compiler-specific ways to accomplish this differently, but that's a different question).Debatable
C
0

C++ destroy a local variable when unwinding stack(return from a function, either with return keyword or with an exception), so it should see one to destroy your scoped_ptr. But in your special case, exception occurred in main so terminate will be called and kill your program before C++ unwind the stack.

void test() {throw 1;}
void main() {
    string sMain;
    test();
}

In above example, sMain will not destroyed because exception cause calling terminate:

sMain constructed
exception occurred: main has no where to go, and it has no handler to handle
    the exception, so it will call `terminate`, but wait we are still at `main`
    so we have no stack unwinding here and sMain will never destroyed!!
Colman answered 14/10, 2012 at 23:58 Comment(1)
Either both sTest and sMain will be destroyed, or neither will. There is no scenario where one will be destroyed and not the either – either stack unwinding occurs or it doesn't.Debatable

© 2022 - 2024 — McMap. All rights reserved.