函数异常规范和标准异常 - foo()throw(Exception)

时间:2012-02-23 17:36:58

标签: c++ exception exception-handling throw

在C ++中,您可以使用以下异常规范声明函数:

int foo() const throw(Exception);

我找到了这两个链接:

但有几件事最终没有答案......

问题1:为什么要添加异常规范?它会带来任何性能提升吗?编译器会有什么不同?因为它似乎就像程序员的信息一样。

问题2:如果我扔掉一些不符合规格的东西会发生什么(会发生什么)?例如:

int foo() throw(int) {
        throw char; // Totally unrelated classes, not types in real
}

问题3:函数/方法不应该抛出任何东西。我发现至少有两种(不同编译器的三种替代语法)指定无异常抛出的方法:

  • int foo() throw();
  • int foo() __attribute(nothrow)__ for gcc
  • int foo() nothrow for visual C ++

哪一个是“正确的”?有什么区别吗?我应该使用哪一个?

问题4:“标准例外”,bad_allocbad_castbad_exceptionbad_typeidios_base::failure

好的bad_alloc是自我解释的,我知道如何(更重要的是何时)使用它(添加到异常规范),但其他的呢?他们都没有真正响铃......他们与哪些“代码片段”相关联?例如,bad_allocnew char[500000]相关联。

问题5:如果我有异常类层次结构,请执行以下操作:

    class ExceptionFileType {
             virtual const char * getError() const = 0;
    };

    class ExceptionFileTypeMissing : public ExceptionFileType {
            virtual const char *getError() cosnt {
                    return "Missing file";
            }
    }

我应该使用:

    int foo() throw(ExceptionFileType);

或者:

    int foo() throw(ExceptionFileTypeMissing,ExceptionFileTypeNotWritable,ExceptionFileTypeNotReadable,...)

注意:带参考的答案会很棒。我正在寻找好的练习技巧。

4 个答案:

答案 0 :(得分:6)

简单的良好做法"提示是:不要使用异常规范。

基本上唯一的例外是可能出现空的异常规范:throw()。这非常有用,在C ++ 11中它被赋予了自己的关键字(noexcept)。它普遍认为,任何非空的异常规范都是一个糟糕的想法。

异常规范(noexcept除外)已被正式弃用 - 与许多已弃用的功能不同,删除此功能会影响到我认为最不足的源代码,最终有可能将其删除(当然不能保证,但无论如何都是相当公平的机会。)

当/如果你抛出异常规范不允许的类型的异常时会发生什么:std::unexpected()被调用。默认情况下,它会调用terminate()。您可以使用std::set_unexpected设置自己的处理程序 - 但您可以合理地做的就是在terminate()之前添加一些日志记录。您的意外处理程序不允许返回。

答案 1 :(得分:3)

问题1

不要打扰。他们是一个坏主意,并在最新版本的语言中被弃用。它们对编译器没有任何好处,因为它们在运行时被检查;如果有的话,在某些情况下它们可能会损害性能。

问题2

将调用名为std::unexpected的函数。默认情况下,这会调用std::terminate;默认情况下,终止程序。如果您真的想要,可以使用std::set_unexpectedstd::set_terminate来安装自己的处理程序,从而更改这两种行为。

问题3

throw()是执行此操作的标准方式;其他是非可移植的编译器扩展。在C ++ 11中,您可以使用noexcept,它提供编译时检查,没有任何东西可以抛出,而不是运行时检查没有任何东西抛出。

问题4

    bad_cast引用失败时,抛出
  • dynamic_cast
  • 当违反异常规范时,
  • bad_exception会在某些奇怪的情况下抛出。
  • 如果评估bad_typeid的参数涉及取消引用空指针,则抛出
  • typeid 当某些操作失败时,输入/输出库(ios_base::failure等)抛出
  • <iostream>

问题5

如果要允许抛出整个层次结构,则只需指定基类。但是你根本不应该使用异常说明符。

答案 2 :(得分:2)

首先,让我们非常清楚异常规范的作用:它是 或多或少像assert无法禁用,断言你 除了其他之外,不会退出该功能 失踪。因此,它的效用远比看上去有限 首先;在大多数情况下(在这种情况下,我无法想象一个 例外),唯一真正有用的保证是throw(),其中 保证不会抛出任何异常;如果你想写 异常安全的代码,你需要这个保证几个低级别 功能

实际上,虽然throw()可以允许一些额外的编译器 优化,通用实现往往导致更少 使用异常规范时的高效代码。在C ++ 11中, throw()已被noexcept取代,大概希望如此 编译器实现者会用它做一些聪明的事情。

编辑:

因为每个人(包括我自己)似乎都错过了你的问题4:

如果不能,bad_alloc函数将抛出

operator new 分配内存。

bad_cast将被dynamic_cast引用到引用中 演员表演失败的情况。 (指针的dynamic_cast返回一个 在这种情况下,空指针。)

当异常规范是时,将抛出

bad_exception 如果异常规范允许bad_exception,则违反。 (换句话说,算了吧。)

如果您尝试将bad_typeid与null一起使用,则会抛出

typeid 指针。

如果您请求输入流,则会抛出

ios_base::failure 如果出现错误。

实际上说:如果你想要恢复,你会抓住bad_alloc 并从内存不足的情况继续。这意味着不常见。 (从内存不足的情况中恢复是非常非常困难的。) 对于bad_cast,它可能更适合使用指针,并测试 null,如果你不确定。没有任何借口可以看到一个 bad_typeid。大多数情况下,您可能希望测试IO错误 显式,而不是配置流来抛出异常;和 设置ios_base::badbit时的异常可能是异常(因为它 代表了一个真正特殊的例外情况。

答案 3 :(得分:1)

问题1和问题2在this question中得到了很长的解决。

问题3和5涵盖在该问题的公认答案中,您根本不使用例外规范。

问题4似乎可以通过在您选择的搜索引擎中键入这些异常名称或查阅优秀C ++书籍的索引来得到充分解决。你对它们有具体的询问吗?