【问题标题】:Throwing an exception from the constructor of an exception in C++从 C++ 中异常的构造函数中抛出异常
【发布时间】:2019-03-11 07:09:30
【问题描述】:

请注意,这个问题与从异常类的构造函数 中抛出异常有关,而不是从任何旧的构造函数中抛出异常。我在 stackoverflow 上找不到重复的问题。

This article 建议不要抛出这样的异常,但我对给出的技术原因持怀疑态度(作者似乎在 cmets 中回溯了原因)。

我将举一个例子,然后讨论我对正在发生的事情的解释,这意味着这样做基本上不应该有问题,至少从技术角度来看是这样。我的主要问题是我的解释是否正确,或者我是否在概念上遗漏了什么。

示例:假设我想将所有可能的异常分为两种类型,User_ErrorCoder_Error。显然,前者表明用户做了他们不应该做的事情,后者表明一些严重的内部检查失败了,有人应该提交错误报告。这是一个(显然过于简化的)草图:

#include <stdexcept>
#include <string>
...

class Coder_Error : public std::runtime_error
{
public:
    Coder_Error( const std::string& msg ) :
       std::runtime_error( msg + "  Please freak out and file a bug report!" )
    {}
};


class User_Error : public std::runtime_error
{
protected:
    static void check_state() const
    {
        ...  // May rely on static members of this class or other classes.
        if ( validation_failed ) {
            throw Coder_Error( "A useful description of the problem." );
        }
    }

public:
    User_Error( const std::string& msg ) : std::runtime_error( msg )
    {
        check_state();
    }
};

现在假设我在User_Error::check_state() 将抛出的情况下执行throw User_Error( "Some useful message." );。会发生什么?

解释: 我的期望是Coder_Error 对象会在遇到throw User_Error( "Some useful message." ) 行中的throw 之前被抛出,因此我们不必担心两个异常同时被抛出,或类似的东西。更明确地说,我希望相关步骤按以下顺序发生。

  1. User_Error::User_Error 将被调用。

  2. User_Error::check_state 将被调用。

  3. Coder_Error::Coder_Error 将被调用。

  4. 第 3 步中创建的 Coder_Error 对象将被抛出。

  5. 堆栈展开将开始,普通执行将停止,因此执行将永远不会达到在步骤 1 中创建的throw User_Error 的程度。(我假设没有错误出现被抓住了。)

这对吗?

【问题讨论】:

  • 并不是说这是不可能的,但我很难想到一个实际情况,在这种情况下,需要有目的地隐藏具有不同类型的异常 sometimes
  • 如果Coder_Error 表示编程错误,那么从std::logic_error 继承可能更直观,因为它更能代表这种情况。 std::runtime_error 与意外情况相关联,而std::logic_error 与编程错误相关联,并且更难且更不容易从中恢复。
  • 如果您有类似 throw User_Error ();User_Error 的投掷物,预计会遇到颠簸。我不会这样做。
  • 我不确定它是否符合条件,但[except.throw]/7 可能与此处相关。
  • 如果你想抛出一个可能会抛出自身的类型,那么请相应地命名它...throw up();

标签: c++ exception constructor throw


【解决方案1】:

据我所知,如果 Coder_Error 对象被封闭的处理程序捕获,那么评估 throw User_Error(...) 具有明确定义的结果并且不会调用 std::terminate 是正确的。 GCC and Clang both agree.

您的推理对于 C++14 及以下版本是正确的。在 C++17 中,由于强制复制省略,User_Error 的构造函数在抛出异常的过程中被调用,即,一旦编译器已经开始评估 throw 部分并且已在某处为异常对象分配了一些存储空间。但是,当构造退出 via 第二个异常时,编译器会清理该存储并继续寻找第二个异常的处理程序。无论哪种情况,结果都如你所说。

请注意,如果在处理异常期间调用的复制构造函数退出通过异常(为异常对象被复制到按值捕获的处理程序中)(see example)。这与您描述的情况不同。

【讨论】:

猜你喜欢
  • 2023-03-11
  • 2011-11-04
  • 1970-01-01
  • 2014-11-03
  • 1970-01-01
  • 2010-10-29
  • 2017-02-09
  • 2022-01-16
相关资源
最近更新 更多