使用arg捕获的异常抛出和抛出有什么区别?

时间:2009-09-26 16:45:49

标签: c++ exception try-catch throw

想象一下两个类似的代码:

try {
  [...]
} catch (myErr &err) {
  err.append("More info added to error...");
  throw err;
}

try {
  [...]
} catch (myErr &err) {
  err.append("More info added to error...");
  throw;
}

这些是否实际相同,或者它们是否以某种微妙的方式不同?例如,第一个是否导致复制构造函数运行,而第二个可能重用相同的对象来重新抛出它?

2 个答案:

答案 0 :(得分:28)

根据排列异常层次结构的方式,通过在throw语句中命名异常变量来重新抛出异常,可以 slice 原始异常对象。

无参数抛出表达式将抛出当前异常对象,保留其动态类型,而带有参数的throw表达式将基于{{>> 参数的 static 类型抛出新异常{{ 1}}。

E.g。

throw

如上所述,程序将输出:

Caught a reference to base
Derived
Caught a reference to base
Base

如果int main() { try { try { throw Derived(); } catch (Base& b) { std::cout << "Caught a reference to base\n"; b.print(std::cout); throw b; } } catch (Base& b) { std::cout << "Caught a reference to base\n"; b.print(std::cout); } return 0; } 替换为throw b,则外部catch也将捕获最初抛出的throw异常。如果内部类通过值而不是通过引用捕获Derived异常,这仍然成立 - 尽管这自然意味着无法修改原始异常对象,因此对Base的任何更改都不会反映在外部块捕获的b异常。

答案 1 :(得分:16)

在根据C ++标准15.1 / 6的第二种情况下,不使用复制构造函数:

  

没有操作数的throw-expression重新抛出正在处理的异常。使用现有临时值重新激活该例外;没有创建新的临时异常对象。异常不再被视为被捕获;因此,uncaught_exception()的值将再次为真。

在第一种情况下,将根据15.1 / 3抛出新的异常:

  

throw-expression初始化一个临时对象,称为异常对象,其类型是通过从throw和adjust的操作数的静态类型中删除任何顶级cv限定符来确定的。   从“T的数组”或“返回T的函数”到“指向T的指针”或“返回T的函数的指针”的类型,   分别。 &LT; ...&GT;临时用于初始化匹配处理程序(15.3)中命名的变量。 throw-expression的类型不应该是   不完整的类型,或指向不完整类型的指针或引用,而不是void *,const void *,   volatile void *,或const volatile void *。除了这些限制和限制   在15.3中提到的类型匹配,throw的操作数被完全视为调用中的函数参数   (5.2.2)或退货声明的操作数。

在这两种情况下,在投掷阶段(15.1 / 5)都需要复制构造函数:

  

当抛出的对象是类对象,并且无法访问用于初始化临时副本的复制构造函数时,程序格式不正确(即使临时对象可能被删除)。   同样,如果该对象的析构函数不可访问,则程序格式错误(即使临时对象可能被删除)。