简体   繁体   English

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

[英]Function exceptions specification and standard exceptions - foo() throw(Exception)

In C++ you may declare function with exception specification like this: 在C ++中,您可以使用如下的异常规范声明函数:

int foo() const throw(Exception);

I found those two links: 我找到了这两个链接:

But several things end up unanswered... 但有几件事最终没有答案......

Question 1: why to add exception specification? 问题1:为什么要添加异常规范? Will it bring any performance increase? 它会带来任何性能提升吗? What will be different for compiler? 编译器会有什么不同? Because it seems just like an information for programmer to me. 因为它对我来说似乎就像程序员的信息。

Question 2: what will happend (what should happen) if I throw something that isn't in specification? 问题2:如果我扔掉一些不符合规格的东西会发生什么(会发生什么)? For example: 例如:

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

Question 3: function/method shouldn't throw anything. 问题3:函数/方法不应该抛出任何东西。 I found at least two (three, alternative syntax for different compilers) ways to specify no exception throwing: 我找到了至少两个(三种,不同编译器的替代语法)方法来指定无异常抛出:

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

Which one is "correct"? 哪一个是“正确的”? Is there any difference? 有什么区别吗? Which one should I use? 我应该使用哪一个?

Question 4: "standart exceptions", bad_alloc , bad_cast , bad_exception , bad_typeid and ios_base::failure . 问题4: “标准异常”, bad_allocbad_castbad_exceptionbad_typeidios_base::failure

Ok bad_alloc is self explaining and I know how (and more importantly when) to use it (add to exception specification), but what about the others? 好的bad_alloc是自我解释,我知道如何(更重要的是)何时使用它(添加到异常规范),但其他的呢? None of them does really ring a bell... Which "code pieces" are they associated with? 他们都没有真正响铃......他们与哪些“代码片段”相关联? Like bad_alloc is associated with new char[500000] . 就像bad_allocnew char[500000]相关联。

Question 5: If I have exception classes hierarchy, like this: 问题5:如果我有异常类层次结构,如下所示:

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

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

Should I use: 我应该使用:

    int foo() throw(ExceptionFileType);

Or: 要么:

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

Note: answers with references would be great. 注意:带参考的答案会很棒。 I'm looking for good practice tips. 我正在寻找好的练习技巧。

The simple "good practice" tip is: don't use exception specifications. 简单的“良好实践”提示是:不要使用异常规范。

Essentially the only exception to that is the possibility of an empty exception specification: throw() . 基本上唯一的例外是可能出现一个空的异常规范: throw() That's sufficiently useful that in C++11 it's been given its own keyword ( noexcept ). 这非常有用,在C ++ 11中它被赋予了自己的关键字( noexcept )。 It's generally agreed that any non-empty exception specification is a lousy idea though. 人们普遍认为,任何非空的异常规范都是一个糟糕的想法。

Exception specifications (other than noexcept ) are officially deprecated -- and unlike many deprecated features, removing this would affect little enough source code that I think there's a good chance it really will eventually be removed (certainly no guarantee, but a pretty fair chance anyway). 正式弃用了异常规范(除了noexcept ) - 并且与许多弃用的功能不同,删除它会影响到足够少的源代码,我认为它最终有可能最终被删除(当然不能保证,但无论如何都是相当公平的机会) )。

As for what happens when/if you do throw an exception of a type not allowed by the exception specification: std::unexpected() gets invoked. 至于当/如果你确实抛出异常规范不允许的类型的异常时会发生什么:调用std::unexpected() By default, that invokes terminate() . 默认情况下,它调用terminate() You can use std::set_unexpected to set your own handler -- but about all you can reasonably do is add some logging before you terminate() . 您可以使用std::set_unexpected设置自己的处理程序 - 但是您可以合理地做的就是在terminate()之前添加一些日志记录。 Your unexpected handler is not allowed to return. 您的意外处理程序不允许返回。

Question 1 问题1

Don't bother. 不要打扰。 They were a bad idea, and were deprecated in the latest version of the language. 他们是一个坏主意,并在最新版本的语言中被弃用。 They give no benefit to the compiler, since they are checked at runtime; 它们对编译器没有任何好处,因为它们在运行时被检查; if anything, they might hurt performance in some cases. 如果有的话,在某些情况下它们可能会损害性能。

Question 2 问题2

A function called std::unexpected will be called. 将调用一个名为std::unexpected函数。 By default, this calls std::terminate ; 默认情况下,这会调用std::terminate ; by default, that terminates the program. 默认情况下,终止程序。 Both of these behaviours can be changed, using std::set_unexpected and std::set_terminate to install your own handler, if you really want to. 如果你真的想要,可以使用std::set_unexpectedstd::set_terminate来安装你自己的处理程序,这两种行为都可以改变。

Question 3 问题3

throw() was the standard way to do it; throw()是执行它的标准方法; the others are non-portable compiler extensions. 其他是非可移植的编译器扩展。 In C++11, you might use noexcept , which gives a compile-time check that nothing can throw, rather than a run-time check that nothing does throw. 在C ++ 11中,您可以使用noexcept ,它提供编译时检查,没有任何东西可以抛出,而不是运行时检查没有任何东西抛出。

Question 4 问题4

  • bad_cast is thrown when a dynamic_cast of a reference fails. 当引用的dynamic_cast失败时抛出bad_cast
  • bad_exception is thrown in some weird circumstances when an exception specification is violated. 当违反异常规范时,在某些奇怪的情况下会抛出bad_exception
  • bad_typeid is thrown if evaluating the argument to typeid involves dereferencing a null pointer 如果评估typeid的参数涉及解除引用空指针,则抛出bad_typeid
  • ios_base::failure is thrown by the input/output library ( <iostream> etc.) when some operations fail 当某些操作失败时,输入/输出库( <iostream>等)抛出ios_base::failure

Question 5 问题5

If you want to allow the entire heirarchy to be thrown, then just specify the base class. 如果要允许抛出整个层次结构,则只需指定基类。 But you shouldn't be using exception specifiers at all. 但是你根本不应该使用异常说明符。

First, let's be very clear what an exception specification does: it's more or less like an assert that can't be disabled, asserting that you will not exit the function with an exception other than the ones missing. 首先,让我们很清楚的异常规范做什么:它或多或少类似的assert不能被禁用,断言你不会比的那些缺少其他异常退出功能。 As such, it's utility is far more limited than it would seem at first; 因此,它的效用远比最初看起来那么有限; for the most part (and in this case, I can't imagine an exception), the only really useful guarantee is throw() , which guarantees that no exception will be thrown; 在大多数情况下(在这种情况下,我无法想象一个例外),唯一真正有用的保证是throw() ,它保证不会抛出任何异常; if you want to write exception safe code, you'll need this guarantee for a few low level functions. 如果你想编写异常安全代码,你需要为一些低级函数提供这种保证。

In practice, although throw() could allow some additional compiler optimizations, the generic implementation tends to result in less efficient code when an exception specification is used. 实际上,尽管throw()可以允许一些额外的编译器优化,但是当使用异常规范时,泛型实现往往会导致代码效率降低。 In C++11, the throw() has been replaced by noexcept , presumably with the hope that compiler implementers will do something intelligent with it. 在C ++ 11中, throw()已经被noexcept取代,可能希望编译器实现者能够用它做一些聪明的事情。

EDIT: 编辑:

Since everyone (including myself) seems to have missed your question 4: 因为每个人(包括我自己)似乎都错过了你的问题4:

bad_alloc will be thrown by the operator new function if it cannot allocate the memory. 如果operator new函数无法分配内存,则将抛出bad_alloc

bad_cast will be thrown by a dynamic_cast to a reference, in the case where the cast fails. bad_cast将由被抛出dynamic_cast到参考,在转换失败的情况。 (A dynamic_cast to a pointer returns a null pointer in such cases.) (在这种情况下,对指针的dynamic_cast会返回空指针。)

bad_exception will be thrown when an exception specification is violated, provided the exception specification allows bad_exception . bad_exception当异常的规范要求将被抛出,提供异常规范允许bad_exception (In other words, forget it.) (换句话说,算了吧。)

bad_typeid will be thrown if you try to use typeid with a null pointer. 如果您尝试使用带有空指针的typeid ,则会抛出bad_typeid

ios_base::failure will be thrown if you request a stream to throw in case of errors. 如果您在发生错误时请求流,则会抛出ios_base::failure

Practically speaking: you'll catch bad_alloc if you want to recover and continue from out of memory situations. 实际上说:如果你想恢复并从内存不足的情况继续下去,你会抓到bad_alloc Which means not very often. 这意味着不常见。 (It's very, very difficult to recover from an out of memory situation.) For bad_cast , it's probably preferrable to use pointers, and test for null, if you aren't sure. (从内存不足的情况中恢复是非常非常困难的。)对于bad_cast ,如果你不确定的话,可能更喜欢使用指针,并测试null。 And there's no excuse for ever seeing a bad_typeid . 没有任何借口可以看到bad_typeid Most of the time, you'll probably want to test IO errors explicitly, rather than configuring the stream to throw exceptions; 大多数情况下,您可能希望显式地测试IO错误,而不是将流配置为抛出异常; and exception when ios_base::badbit is set might be an exception (since it represents a truly exceptional case of a hardward fault). 设置ios_base::badbit时的异常可能是一个异常(因为它代表了一个特殊情况下的硬故障)。

Questions 1 and 2 are addressed at some length in this question . 问题1和2在这个问题中有一定篇幅。

Questions 3 and 5 are covered by the recommendation, in the accepted answer to that question, that you don't use exception specifications at all. 问题3和5包含在该问题的公认答案中,即您根本不使用例外规范。

Question 4 seems to be adequately addressed by typing those exception names into the search engine of your choice, or consulting the index of a good C++ book. 问题4似乎可以通过在您选择的搜索引擎中键入这些异常名称或查阅优秀C ++书籍的索引来得到充分解决。 Do you have a specific query about them? 你有关于它们的具体问题吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM