In the following code:
#include <memory>
#include <iostream>
void mydeallocator(int * x) {
std::cerr << "Freeing memory" << std::endl;
delete x;
}
struct Foo {
std::unique_ptr <int,std::function <void(int*)>> x;
Foo(bool fail) : x(new int(1),mydeallocator) {
if(fail)
throw std::runtime_error("We fail here");
}
};
int main() {
{auto foo1 = Foo(false);}
{auto foo2 = Foo(true);}
}
It appears that memory is not being deallocated properly when Foo(true) is called. Namely, when we compile and run this program, we have the result:
Freeing memory
terminate called after throwing an instance of 'std::runtime_error'
what(): We fail here
Aborted
I believe that the message Freeing memory should be called twice. Basically, according to this question and the ISO C++ folks here and here, my understanding is that the stack should unwind on the constructor for Foo and that x should call its destructor, which should call mydeallocator. Certainly, this is not happening, so why is the memory not being freed?
Your original code throw; when you have nothing to rethrow. That causes std::terminate to be called; the stack is not unwound (and hence the destructors don't run).
Your new code throws an exception without handling it. In that case whether the stack is unwound is implementation-defined, so it's still perfectly conforming to terminate() right away. [except.terminate], emphasis mine:
In some situations exception handling must be abandoned for less subtle error handling techniques. [ Note: These situations are:
- when the exception handling mechanism, after completing the initialization of the exception object but before activation of a handler for the exception (15.1), calls a function that exits via an exception, or
- when the exception handling mechanism cannot find a handler for a thrown exception (15.3), or
- when the search for a handler (15.3) encounters the outermost block of a function with a noexcept-specification that does not allow the exception (15.4), or
- when the destruction of an object during stack unwinding (15.2) terminates by throwing an exception, or
- when initialization of a non-local variable with static or thread storage duration (3.6.2) exits via an exception, or
- when destruction of an object with static or thread storage duration exits via an exception (3.6.3), or
- when execution of a function registered with
std::atexitorstd::at_quick_exitexits via an exception (18.5), or- when a throw-expression (5.17) with no operand attempts to rethrow an exception and no exception is being handled (15.1), or
- when
std::unexpectedexits via an exception of a type that is not allowed by the previously violated exception specification, and std::bad_exception is not included in that exception specification (15.5.2), or- when the implementation’s default unexpected exception handler is called (D.8.1), or
- when the function
std::nested_exception::rethrow_nestedis called for an object that has captured no exception (18.8.6), or- when execution of the initial function of a thread exits via an exception (30.3.1.2), or
- when the destructor or the copy assignment operator is invoked on an object of type
std::threadthat refers to a joinable thread (30.3.1.3, 30.3.1.4), or- when a call to a
wait(),wait_until(), orwait_for()function on a condition variable (30.5.1, 30.5.2) fails to meet a postcondition. —end note ]In such cases,
std::terminate()is called (18.8.3). In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound beforestd::terminate()is called. In the situation where the search for a handler (15.3) encounters the outermost block of a function with a noexcept-specification that does not allow the exception (15.4), it is implementation-defined whether the stack is unwound, unwound partially, or not unwound at all beforestd::terminate()is called. In all other situations, the stack shall not be unwound beforestd::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 tostd::terminate().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With