在哪种情况下,以下两个代码不相同?
{
// some code, may throw and/or have side effects
}
try {
// same code as above
} catch(...) {
throw;
}
编辑为了澄清,我对(i)偏离上述模式(例如catch块中的更多代码)以及(ii)打算邀请光顾的评论不感兴趣正确使用try
- catch
块。
我正在寻找一个参考C ++标准的合格答案。 Cheers and hth. - Alf对this answer of mine的评论提示了这个问题,并且没有进一步解释上述代码不等同。
编辑他们确实与众不同。堆栈取消将在后者中完成,但不一定在前者中,取决于是否在运行时找到异常处理程序(某些catch
块在堆栈的上方)。
答案 0 :(得分:17)
后者要求堆栈展开,而在前者中,如果堆栈被展开则它是实现定义的。
相关标准报价(全部来自N3337):
[except.ctor]/1:
当控件从throw-expression传递给处理程序时,将为所有自动对象调用析构函数 从输入try块开始构造。自动对象以相反的顺序销毁 完成他们的建设。
[except.ctor]/3:
为从try块到a的路径构造的自动对象调用析构函数的过程 throw-expression被称为“堆栈展开”。[...]
[except.terminate]/2:
[当异常处理机制找不到抛出异常的处理程序时],调用std::terminate()
(18.8.3)。在没有找到匹配处理程序的情况下, 它是实现定义的,无论堆栈是否在调用std::terminate()
之前展开。 [...]
因此,如果你想保证你的自动对象在未处理的异常的情况下运行它们的析构函数(例如,某些持久存储必须在销毁时变异),那么try {/*code*/} catch (...) {throw;}
将会这样做,但是{{ 1}}不会。
答案 1 :(得分:7)
Cheers and hth. - Alf's comment上的Elaboratring:
来自http://en.cppreference.com/w/cpp/error/terminate:
当异常处理时,C ++运行时调用std :: terminate() 由于以下任何原因而失败:
1)抛出异常但未捕获(它是实现定义的 在这种情况下是否完成任何堆栈展开)
如果您的
,可能不会发生堆栈展开{
// some code, may throw and/or have side effects
}
不在另一个try/catch
区块内。
示例:
struct A {
A() {}
~A() { std::cout << "~A()" << std::endl; }
};
int main()
{
// try {
A a;
throw 1;
// } catch(...) {
// throw;
// }
}
Under coliru's gcc 5.2.0 with -O2无法打印~A()
,而try/catch
会打印。
UPD :关于您对单独编译单元的编辑,仅使用我的本地gcc 4.8.2进行测试,行为是相同的:如果没有catch
则没有堆栈展开。具体的例子:
a.h
:
struct A {
A();
~A();
};
void foo();
a.cpp
:
#include <iostream>
using namespace std;
struct A {
A() {}
~A() { cout << "~A()" << endl; }
};
void foo() {
A a;
throw 1;
}
main.cpp
:
#include "a.h"
int main () {
//try {
foo();
//} catch(...) {
// throw;
//}
}
我认为是否存在catch
是在运行时确定的,因为无论如何在运行时抛出异常时,程序需要查找catch
。因此,选择是否在运行时解除堆栈也是有意义的。
答案 2 :(得分:3)
语义上是等价的。我不确定,如果某些编译器可能无法优化不必要的try
- catch
。我希望退出try
- catch
块。这通常会使代码更容易解开。
答案 3 :(得分:3)
假设&#34;一些代码&#34;没有表现出未定义的行为(在这种情况下,无论是否添加try / catch块,所有投注均已关闭),最终结果将没有差异。它是技术上实现定义的(即实现必须记录它的作用),如果永远不会捕获异常,是否会发生堆栈展开,但是在这种情况下还没有任何实现不报废堆栈的报告。如果发生堆栈展开,则所有局部变量都将超出范围,而具有析构函数的变量将调用析构函数。
可能存在或可能没有可衡量的性能差异,与某些代码&#34;之前的设置开销相关联。执行,捕获异常(如果有)和重新抛出,以及任何其他清理。差异将取决于编译器,并且对于旧的编译器,可能具有重要意义。对于现代编译器,开销的差异(如果有的话)会稍微减少,因为异常和异常处理的实现技术已得到改进。
答案 4 :(得分:1)
如果你发现基本的例外情况,他们就完全一样了。如果您在抛出之前执行某些操作(如日志记录),则只能从捕获和重新抛出异常中受益。但你不应该抓住异常。现在只能抓住你现在如何恢复的例外情况。
答案 5 :(得分:-1)
如果没有像RAII惯用语那样管理资源,可以在重新抛出之前在catch块中执行一些有意义的清理
{
// some code, may throw and/or have side effects
}
try {
// same code as above
} catch(...) {
//Some meaningful clean up can be performed here if resources not managed as RAII idiom
throw;
}