Answers:
不,这不是一种好的做法。相反,通常认为这是一个坏主意。
http://www.gotw.ca/publications/mill22.htm详细介绍了原因,但是部分原因是编译器无法强制执行此操作,因此必须在运行时进行检查,通常不受欢迎的。而且在任何情况下都无法很好地支持它。(MSVC忽略了异常规范,throw()除外,该规范被解释为保证不会引发异常。
Jalf已经链接到它了,但是GOTW很好地说明了为什么异常规范没有人们希望的有用:
int Gunc() throw(); // will throw nothing (?)
int Hunc() throw(A,B); // can only throw A or B (?)
评论正确吗?不完全的。
Gunc()
可能确实会丢东西,并且Hunc()
可能会丢掉A或B以外的东西!编译器只是保证在它们发生错误时击败他们……哦,并且在大多数情况下也使您的程序失去意义。
这就是它的最终结果,您可能最终会调用,terminate()
并且您的程序死于快速但痛苦的死亡。
GOTW的结论是:
因此,这似乎是我们作为一个社区迄今为止所获得的最佳建议:
- 道德准则1:永远不要编写异常规范。
- 道德2:除了可能是一个空洞的人以外,但如果我是你,我什至会避免。
要为该问题的所有其他答案增加更多的价值,您应该在该问题上花费几分钟:以下代码的输出是什么?
#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;
}
}
答:如前所述这里,程序调用std::terminate()
,因此没有任何异常处理程序将被调用。
详细信息:my_unexpected()
调用了第一个 函数,但是由于它没有为throw_exception()
函数原型重新抛出匹配的异常类型,因此最后std::terminate()
调用了。因此完整的输出如下所示:
user @ user:〜/ tmp $ g ++ -oexcept.testexcept.test.cpp
user @ user:〜/ tmp $ ./except.test
很好-这是
在抛出'int'
中止实例后终止的意外终止倾销)
好吧,在谷歌搜索该抛出规范时,我看了一下这篇文章:-(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 (...)
{
}
}
};
当编译器看到此情况时,使用“ throw()”属性,编译器可以完全优化“ bar”变量,因为它知道无法从MethodThatCannotThrow()引发异常。如果没有throw()属性,则编译器必须创建“ bar”变量,因为如果MethodThatCannotThrow引发异常,则异常处理程序可能/将取决于bar变量的值。
另外,诸如prefast之类的源代码分析工具可以(并且将)使用throw()批注来改善其错误检测功能-例如,如果您有try / catch且您调用的所有函数都标记为throw(),您不需要try / catch(是的,如果您以后调用可能抛出的函数,则会出现问题)。
内联函数的不抛出规范只能返回一个成员变量,并且不可能抛出异常,某些编译器可能会使用它们进行悲观化(与优化相反的虚假单词),从而对性能产生不利影响。Boost文献中对此进行了描述:异常规范
对于某些编译器,如果进行了正确的优化,并且对非内联函数的无约束规范可能是有益的,并且对该函数的使用会以合理的方式影响性能。
在我看来,是否使用它是一个非常挑剔的人发出的电话,它是性能优化工作的一部分,也许是使用性能分析工具。
上面链接的引号是针对那些匆忙的人的(包含从天真的编译器指定对内联函数的抛出异常的不良影响的示例):
异常规范的原理
有时会对异常规范[ISO 15.4]进行编码,以指示可能会抛出哪些异常,或者是因为程序员希望它们可以提高性能。但是从智能指针考虑以下成员:
T&运算符*()const throw(){return * ptr; }
该函数不调用其他函数。它仅处理诸如指针之类的基本数据类型,因此,永远无法调用异常规范的运行时行为。该功能完全向编译器公开;实际上,它是声明为内联的。因此,智能编译器可以轻松推断出这些函数无法引发异常,并且可以基于空的异常规范进行相同的优化。但是,“哑”编译器可能会进行各种悲观化。
例如,如果存在异常规范,某些编译器会关闭内联。一些编译器添加了try / catch块。这样的悲观化可能会造成性能灾难,从而使代码在实际应用中无法使用。
尽管起初很吸引人,但是异常规范往往会带来后果,需要仔细思考才能理解。异常规范的最大问题是程序员使用它们时,就像它们具有程序员想要的效果一样,而不是实际上具有效果。
非内联函数是“不抛出任何异常”异常规范的地方,这对于某些编译器可能会有所帮助。