为什么在构造函数调用异常后没有释放unique_ptr?

时间:2015-07-24 05:09:56

标签: c++ c++11 c++14

在以下代码中:

$filename = "Registrations.csv";
$fp = fopen('php://output', 'w');
$header = array("Sr No","FirstName","RegNo","Address","Contact","email");
header('Content-type: application/csv');
header('Content-Disposition: attachment; filename='.$filename);
fputcsv($fp, $header);  
$query = "select FirstName,RegNo,Address,Contact,email from  Registration";     
$result = mssql_query($query);
while($row = mssql_fetch_row($result)) {
  fputcsv($fp, $row);
}
exit;   

在调用#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 应该被调用两次。基本上,根据这个question和ISO C ++人员herehere,我的理解是堆栈应该在Freeing memoryFoo的构造函数上展开。应该调用它的析构函数,它应该调用x。当然,这不会发生,为什么内存没有被释放?

1 个答案:

答案 0 :(得分:21)

当您无需重新抛出时,您的原始代码throw;。这导致std::terminate被调用;堆栈没有解开(因此析构函数不会运行)。

您的新代码会抛出异常而不处理它。在这种情况下,堆栈是否被展开是实现定义的,因此它仍然完全符合terminate()。 [except.terminate],强调我的:

  

在某些情况下,必须放弃异常处理   微妙的错误处理技术。 [注意:这些情况是:

     
      
  • 当异常处理机制完成异常对象的初始化之后但在激活之前   异常处理程序(15.1),调用一个通过一个退出的函数   例外,或
  •   
  • 当异常处理机制找不到抛出异常的处理程序(15.3)
  • 时   
  • 当搜索处理程序(15.3)遇到函数的最外面的块时,noexcept-specification不允许   例外(15.4)或
  •   
  • 当堆栈展开期间对象的销毁(15.2)因抛出异常而终止时,或
  •   
  • 当静态或线程存储持续时间(3.6.2)的非局部变量的初始化通过异常退出时,或
  •   
  • 当具有静态或线程存储持续时间的对象的销毁通过异常(3.6.3)或
  • 退出时   
  • 执行注册std::atexitstd::at_quick_exit的功能时,通过例外(18.5)或
  • 退出   
  • 当没有操作数的 throw-expression (5.17)尝试重新抛出异常并且没有处理异常时(15.1),或
  •   
  • std::unexpected通过先前违反的异常规范不允许的类型的异常退出时,   std :: bad_exception未包含在该异常规范中   (15.5.2)或
  •   
  • 调用实现的默认意外异常处理程序(D.8.1)或
  • 时   
  • 为没有捕获异常的对象调用函数std::nested_exception::rethrow_nested(18.8.6)或
  •   
  • 当执行线程的初始函数时,通过异常(30.3.1.2)或
  • 退出   
  • 在类型std::thread引用可连接线程的对象上调用析构函数或复制赋值运算符时   (30.3.1.3,30.3.1.4)或
  •   
  • 当条件变量(30.5.1,30.5.2)上的wait()wait_until()wait_for()函数调用无法满足时   后置条件。 - 结束记录]
  •   
     

在这种情况下,调用std::terminate()(18.8.3)。 在这种情况下   在没有找到匹配处理程序的地方,它是实现定义的   堆栈是否在std::terminate()之前展开   叫。在搜索处理程序的情况下(15.3)   遇到一个函数的最外面的块    noexcept-specification ,它不允许异常(15.4),它是实现定义的,是否堆栈是展开的,展开的   在std::terminate()被调用之前,部分或根本没有解开。   在所有其他情况下,堆栈不得在之前解开   调用std::terminate()不允许实施   完成堆栈基于确定的过早退绕   展开过程最终会调用std::terminate()