【问题标题】:Should I throw an exception我应该抛出异常吗
【发布时间】:2013-05-20 00:16:16
【问题描述】:

我是 C# 程序员,但现在我想更深入地了解 C++。
我知道 C++ 的基础知识,但我不知道如何处理错误。

例如:我正在编写一个库。我创建了一个构造函数,它请求一个整数作为参数。
如果该整数大于 50,则为错误。 在 C# 中我会抛出 ArgumentOutOfRange 异常,但在 C++ 中我应该怎么做?

【问题讨论】:

  • @stefan,在 C++ 中不使用异常来控制程序流。远离问题 - 建立一个工厂可能是个好主意,它会执行所有必需的检查并避免因错误原因使用异常

标签: c++ exception error-handling


【解决方案1】:

来自 C++ 常见问题解答:[17.8] How can I handle a constructor that fails?

摘录:

抛出异常。

构造函数没有返回类型,因此无法使用 返回码。因此,发出构造函数失败信号的最佳方法是 抛出异常。如果您没有使用的选项 例外,“最不坏”的解决方法是将对象放入 “僵尸”状态通过设置内部状态位使对象动作 有点像它已经死了,即使它在技术上还活着。

因此,对于您的情况,投掷 std::invalid_argumentstd::out_of_range 是完全可以接受的。如果这对您的情况有益,您也可以抛出 自定义异常。在 C++ 常见问题解答中,请参阅:[17.12] What should I throw?

【讨论】:

    【解决方案2】:

    在 C# 中我会抛出 ArgumentOutOfRange 异常,但在 c++ 中我应该怎么做?

    首先,您应该考虑这是否不应该是您的函数的前提条件,将检查值是否在范围内的责任留给调用者。

    如果您决定使用此选项,那么使用超出范围的值调用函数将是未定义的行为,并且在函数内部您可以有一个调试断言来帮助您发现可能的误用 - 而无需抛出任何例外。

    另一方面,如果您决定该函数应该有一个广泛的合同,并通过在参数超出允许范围时抛出异常以明确定义的方式做出反应,那么您可以抛出 std::out_of_range 异常.

    例如:我正在写一个库 [...]

    如果您正在编写一个库,这意味着您不知道客户在性能和健壮性方面的确切要求,您可以考虑提供 两个这样的函数 - 一个具有广泛的合同,抛出异常,并且具有假设客户端提供有意义的输入的狭窄合同。

    这样,库的用户可以根据他们的用例决定是否可以支付每次调用函数时检查输入正确性的开销。

    这是,例如,C++ 标准库为std::vector 采用的策略,它提供了一个不抛出的operator[],用于根据索引访问集合的元素(此函数未定义)索引越界时的行为),以及执行索引检查并在索引越界时抛出异常的成员函数at()

    【讨论】:

    • 到处都是好帖子。 +1 断言(前置/后置条件)、std::vector 示例和选项。
    • @Freddy:很高兴你发现它有帮助
    • 您关于异常的冗长且绝对正确的答案可能会导致 OP 走向错误的一方。在这种情况下也应该考虑 Kerrek SB 的下一个答案,以避免 cmets,例如从 @stefan 到原始问题
    • 什么是窄合同或宽合同?
    • @AndyProwl By (stackoverflow.com/questions/30222608/…) 我假设具有广泛合同的函数永远不会有未定义的行为(所以,它永远不会抛出?)而狭窄的可能是未定义的,具体取决于调用条件.但是你写过:“一个抛出异常的广泛合同”......你能解释一下吗?谢谢!
    【解决方案3】:

    这取决于是否可以将大于 50 的整数作为正常程序流的一部分传递给构造函数,或者这是否是异常情况。但一般来说,使对象构造失败的唯一方法是抛出异常。

    您的用户代码可能如下所示:

    int n = parse_user_input()
    
    if (n < 50)
    {
        Foo x(n);
        x.do_cool_stuff();
    }
    else
    {
        // report user error
    }
    

    也就是说,您实际上并没有为正常的控制流使用异常。使用这种代码模式,如果参数超出范围,Foo::Foo(int) 会抛出异常。

    您可以在&lt;stdexcept&gt; 中找到有用的标准异常类。

    【讨论】:

      【解决方案4】:

      与 C# 中的相同:抛出异常。这是防止对象被构造的唯一方法。

      std::invalid_argument 是一个很好的标准选择,用于抛出什么。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-05-02
        • 2019-01-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多