2009-09-26 104 views
19

试想两个相似的代码段:抛出和抛出arg捕获异常有什么区别?

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; 
} 

有效这些是相同的,还是他们在一些微妙的方式有什么不同?例如,第一个是否会导致拷贝构造函数被运行,而第二个可能会重用同一个对象来重新抛出呢?

回答

26

取决于你如何安排你的异常层次结构,重新抛出由throw语句命名异常变量的异常可能原来的异常对象。

一个无参数的掷表达将抛出当前异常对象保持其动态型,而用自变量的掷表达将根据静态类型的参数throw的抛出一个新的异常。

E.g.

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; 
} 

如上写入时,程序将输出:

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

如果throw b是具有throw替换,则外抓也将赶上原来抛出Derived异常。如果内部类通过值而不是引用捕获异常,这仍然成立 - 尽管自然这意味着原始异常对象不能被修改,所以对b的任何更改都不会反映在由外部块捕获的Derived异常中。

+1

啊,我完全忘了切片!该死的,这很重要!感谢您提出这个问题。 +1(尽管我认为当你写下“...保留原始静态类型...”时,你的意思是_dynamic_type。所谓_dynamic type_,毕竟如果不是_“original static type”_。) - – sbi 2009-09-26 23:58:36

+1

太棒了回答,我也完全忘记了这一点。 – GManNickG 2009-09-27 00:10:29

+0

我很高兴别人遇到_slicing_问题;) – 2009-09-27 00:37:22

16

在根据C++标准15.1/6拷贝构造第二种情况下不使用:

的throw-表达与没有操作数的重新抛出正在处理的异常。该例外情况在现有的临时情况下重新激活;没有新的临时异常对象被创建。这个例外不再被认为是被捕获的;因此,uncaught_exception()的值将再次为真。

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

的throw-表达初始化临时对象,称为异常对象,其中所述类型的通过去除任何确定从“T的数组”或“返回T的函数”到“指向T的指针”或“返回T的函数的指针”的类型,分别为 和 的静态类型的顶级cv限定符。 < ...>临时用于初始化匹配处理程序(15.3)中指定的变量。除了void *,const void *, volatile void *或const volatile void *以外,throw-expression的类型不应为 不完整类型,或指向不完整类型的指针或引用。除了这些限制和对15.3中提到的 类型匹配的限制之外,throw的操作数在调用 (5.2.2)或返回语句的操作数时完全作为函数参数处理。

在两种情况下复制构造需要在掷阶段(15.1/5):

当抛出的对象是一个类对象,和用于初始化临时副本拷贝构造是不可访问,该程序是不合格的(即使临时对象可能被删除)。 同样,如果该对象的析构函数不可访问,则该程序是不合格的(即使临时对象可能被删除)。

+0

由于无论如何都需要访问原始引发的异常,因此我认为在这种情况下这不是问题。但你的答案仍然很好。 +1 – sbi 2009-09-26 17:18:32

+0

是的,但在第一种情况下复制c-tor将被使用两次。 – 2009-09-26 17:20:04

+1

它说什么时候声明指定了一个类的类型。但在他的情况下,它指定了一个引用类型:)引用将被直接绑定并引用异常对象。因此,我们只需要在投掷时需要一个副本,而不是在这种情况下捕获(例如,当基本副本构造函数受保护时,这会有所不同)。 – 2009-09-26 17:25:42