在函数签名中抛出关键字

Kon*_*tin 185 c++ exception

throw在函数签名中使用C++ 关键字被认为是不良做法的技术原因是什么?

bool some_func() throw(myExc)
{
  ...
  if (problem_occurred) 
  {
    throw myExc("problem occurred");
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

jal*_*alf 116

不,这不是好习惯.相反,它通常被认为是一个坏主意.

http://www.gotw.ca/publications/mill22.htm详细介绍了原因,但问题部分在于编译器无法强制执行此操作,因此必须在运行时检查,这通常是不可取的.并且在任何情况下都没有得到很好的支持.(MSVC忽略了除throw()之外的异常规范,它将其解释为保证不会抛出异常.

  • 是.除了throw(myEx)之外,还有更好的方法可以在代码中添加空格. (21认同)
  • 但那么记录的目的呢?此外,即使您尝试,也会被告知您将永远不会捕获哪些例外情况. (6认同)
  • 是的,刚刚发现异常规范的人通常认为它们的工作方式与Java类似,编译器可以在Java中执行它们.在C++中,这不会发生,这使得它们变得不那么有用. (4认同)
  • 我认为在编写代码时代码非常强大.(评论可以说谎).我所指的"记录性"效应是,您将确定您可以捕获哪些例外,而其他例外则不能. (3认同)

sth*_*sth 50

Jalf已经与之相关联,但是GOTW非常清楚为什么异常规范没有人们希望的那样有用:

int Gunc() throw();    // will throw nothing (?)
int Hunc() throw(A,B); // can only throw A or B (?)
Run Code Online (Sandbox Code Playgroud)

评论是否正确?不完全的.Gunc()可能确实会扔东西,并且Hunc()可能会抛出A或B以外的东西!编译器只是保证在他们这样做的时候击败他们毫无意义......哦,并且在大多数情况下也打败你的程序.

这就是它归结为什么,你可能最终会打电话给terminate()你,你的程序会快速而痛苦地死去.

GOTW的结论是:

所以这就是我们社区今天所学到的最佳建议:

  • 道德#1:永远不要写异常规范.
  • 道德#2:除了可能是空的,但如果我是你,我甚至会避免.

  • @Ken:关键在于编写异常规范主要是负面后果.唯一的积极作用是它向程序员显示可能发生的异常,但由于编译器没有以合理的方式检查它,因此容易出错,因此不值得. (3认同)
  • 不正确.应该编写异常规范,但想法是传达调用者应该尝试捕获的错误. (2认同)

Joh*_*Doe 29

为了向这个问题的所有其他答案添加更多的价值,我们应该在问题中投入几分钟:以下代码的输出是什么?

#include <iostream>
void throw_exception() throw(const char *)
{
    throw 10;
}
void my_unexpected(){
    std::cout << "well - this was unexpected" << std::endl;
}
int main(int argc, char **argv){
    std::set_unexpected(my_unexpected);
    try{
        throw_exception();
    }catch(int x){
        std::cout << "catch int: " << x << std::endl;
    }catch(...){
        std::cout << "catch ..." << std::endl;
    }
}
Run Code Online (Sandbox Code Playgroud)

答:如前所述这里,程序调用std::terminate(),因此没有任何异常处理程序将被调用.

详细信息:my_unexpected()调用第一个 函数,但由于它不会为throw_exception()函数原型重新抛出匹配的异常类型,因此最终会std::terminate()被调用.所以完整的输出看起来像这样:

user @ user:〜/ tmp $ g ++ -o except.test except.test.cpp
user @ user:〜/ tmp $ ./except.test
well - 这是
在抛出'int'实例后调用的意外终止
(core倾倒)


Pao*_*sco 11

throw说明符的唯一实际效果是,如果myExc函数抛出不同的东西,std::unexpected将被调用(而不是正常的未处理异常机制).

要记录函数可以抛出的异常类型,我通常会这样做:

bool
some_func() /* throw (myExc) */ {
}
Run Code Online (Sandbox Code Playgroud)

  • 注意调用std :: unexpected()通常会导致对std :: terminate()的调用以及程序的突然结束也很有用. (5认同)

Pra*_*hal 9

好吧,在谷歌搜索这个抛出规范时,我看了看这篇文章: - (http://blogs.msdn.com/b/larryosterman/archive/2006/03/22/558390.aspx)

我也在这里复制它的一部分,以便将来可以使用它,而不管上述链接是否有效.

   class MyClass
   {
    size_t CalculateFoo()
    {
        :
        :
    };
    size_t MethodThatCannotThrow() throw()
    {
        return 100;
    };
    void ExampleMethod()
    {
        size_t foo, bar;
        try
        {
            foo = CalculateFoo();
            bar = foo * 100;
            MethodThatCannotThrow();
            printf("bar is %d", bar);
        }
        catch (...)
        {
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

当编译器看到这个时,使用"throw()"属性,编译器可以完全优化"bar"变量,因为它知道没有办法从MethodThatCannotThrow()抛出异常.如果没有throw()属性,编译器必须创建"bar"变量,因为如果MethodThatCannotThrow抛出异常,异常处理程序可能/将取决于bar变量的值.

此外,像prefast这样的源代码分析工具可以(并且将会)使用throw()注释来改进它们的错误检测功能 - 例如,如果你有一个try/catch,你调用的所有函数都被标记为throw(),你不需要try/catch(是的,如果你以后调用一个可以抛出的函数,这会有问题).


Gre*_*g D 8

当投掷规范被添加到语言时它是出于最好的意图,但实践已经证明了一种更实用的方法.

使用C++,我的一般经验法则是仅使用throw规范来指示方法不能抛出.这是一个有力的保证.否则,假设它可以抛出任何东西.