为什么在构造函数调用异常后 unique_ptr 没有被释放?
Why is a unique_ptr not freed after a constructor calls an exception?
在下面的代码中:
#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);}
}
调用 Foo(true)
时,似乎内存未正确释放。也就是说,当我们编译 运行 这个程序时,我们得到的结果是:
Freeing memory
terminate called after throwing an instance of 'std::runtime_error'
what(): We fail here
Aborted
我认为消息 Freeing memory
应该被调用两次。基本上,根据这个 question and the ISO C++ folks here and here,我的理解是堆栈应该在 Foo
的构造函数上展开并且 x
应该调用它的析构函数,后者应该调用 mydeallocator
。当然,这并没有发生,那么为什么内存没有被释放?
你的原始代码 throw;
当你没有什么可以重新抛出时。这导致 std::terminate
被调用;堆栈没有展开(因此析构函数没有 运行)。
您的新代码抛出异常但未进行处理。在那种情况下,堆栈是否展开是实现定义的,因此它仍然完全符合 terminate()
的要求。 [except.terminate],强调我的:
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::atexit
or std::at_quick_exit
exits 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::unexpected
exits 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_nested
is 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::thread
that refers to a joinable thread
(30.3.1.3, 30.3.1.4), or
- when a call to a
wait()
, wait_until()
, or wait_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 before std::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 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()
.
在下面的代码中:
#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);}
}
调用 Foo(true)
时,似乎内存未正确释放。也就是说,当我们编译 运行 这个程序时,我们得到的结果是:
Freeing memory
terminate called after throwing an instance of 'std::runtime_error'
what(): We fail here
Aborted
我认为消息 Freeing memory
应该被调用两次。基本上,根据这个 question and the ISO C++ folks here and here,我的理解是堆栈应该在 Foo
的构造函数上展开并且 x
应该调用它的析构函数,后者应该调用 mydeallocator
。当然,这并没有发生,那么为什么内存没有被释放?
你的原始代码 throw;
当你没有什么可以重新抛出时。这导致 std::terminate
被调用;堆栈没有展开(因此析构函数没有 运行)。
您的新代码抛出异常但未进行处理。在那种情况下,堆栈是否展开是实现定义的,因此它仍然完全符合 terminate()
的要求。 [except.terminate],强调我的:
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::atexit
orstd::at_quick_exit
exits 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::unexpected
exits 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_nested
is 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::thread
that 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()
.