【问题标题】:Why exceptions from unique_ptr destructor terminates the program?为什么 unique_ptr 析构函数的异常会终止程序?
【发布时间】:2016-06-13 11:11:50
【问题描述】:

看看这段代码,它会导致程序在没有捕获异常的情况下终止。

#include <iostream>
#include <string>
#include <memory>
#include <stdexcept>
using namespace std;

struct test {
  ~test() noexcept(false) {
    throw runtime_error("-my-cool-exception-");
  }
};

int main()
{
  try {
    auto ptr = unique_ptr<test>(new test());
    //test t; // this is ok, without unique_ptr<test> it works fine.
  }
  catch(exception& e) {
    cout << "this is not called, the program is aborted";
    cout << e.what() << endl;
  }
  return 0;
}

这个问题不同于堆栈溢出问题:throwing exceptions out of destructor

不同的是,只有当我使用unique_ptr&lt;test&gt; 时,才不会捕获到异常。

您可以在这里查看实时代码,编辑和编译http://cpp.sh/9sk5m

【问题讨论】:

  • 因为20.10.1.2.2要求删除不抛出异常。你违反了图书馆的先决条件。
  • 我有一个完全依赖于此的库,不能破坏它。有解决方法吗?谢谢。
  • 解决方法:修复您的库,在it bites you any further 之前立即修复。
  • 如果您从析构函数中抛出异常,您的程序将无法继续。异常意味着“停止你正在做的任何事情并跳转到最近的处理程序”,但你不能停止破坏一个对象,这没有任何意义。当您停止制作一个对象时,即从构造函数中抛出,您会破坏已经制作的部分,语言会处理这些。但是,停止摧毁一个物体到底意味着什么?重新制作损坏的零件?这不可能。让它处于半毁状态? C++ 中没有半毁坏的对象。您唯一可以明智地做的就是中止。
  • @Ven 技术上没问题,也就是说,标准没有禁止,但逻辑上你的对象处于不明确的状态。

标签: c++ c++11 unique-ptr


【解决方案1】:

这是标准要求的。根据unique.ptr.single.dtor, 20.10.1.2.2.1

要求:表达式 get_deleter()(get()) 应具有良好的格式,应具有明确定义的行为,并且不应抛出异常。 [ 注意:使用 default_delete 需要 T 是一个完整的类型。 ——尾注]

(强调我的)

所以说真的,你的析构函数本身是否有noexcept(false) 并不重要。在这种情况下,禁止抛出 - 最终决定。

这是std:: 的一般情况——除非另有说明。根据res.on.functions, 17.6.4.8.2.4

特别是,在以下情况下效果是不确定的: [...] 如果任何替换函数或处理函数或析构函数操作通过异常退出,除非在适用的必需行为中明确允许:段落

(强调我的)

注意:通常,您可以使用noexcept(false) 抛出析构函数。但是,这是非常危险的,因为如果堆栈因抛出异常而展开,则会调用 std::terminate。 根据except.ctor, 15.2.1

当控制从抛出异常的点传递到处理程序时,析构函数被本节中指定的进程调用,称为堆栈展开。 如果堆栈展开直接调用的析构函数以异常退出,则调用 std::terminate ([except.terminate])。 [注意:因此,析构函数通常应该捕获异常,而不是让它们传播到析构函数之外。 ——尾注]

(强调我的)

See it live on Coliru.

【讨论】:

  • 应该注意的是,即使你给你的析构函数noexcept(false),这并不能改变unique_ptr的析构函数仍然是noexcept的事实。所以异常会在那里被吞没。
  • "swallowed" 意味着它消失了。它没有。
  • 好的,“explode”可能是比“swallow”更准确的术语;)但无论哪种方式,您都不会看到该异常到达您的 catch 声明,无论您有多少使用noexcept(false)
  • @NicolBolas 为此添加了一句话。
  • 除了unique_ptr-specific 规则之外,标准库调用的任何析构操作都不得抛出异常,除非特别允许 ([res.on.functions]/2.4)。
猜你喜欢
  • 2010-12-25
  • 1970-01-01
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-28
  • 1970-01-01
相关资源
最近更新 更多