C++ catch块 - 按值还是按引用捕获异常?

122

可能是重复问题:
如何在C++中通过指针捕获异常

我通常通过值来捕获异常,例如:

try{
...
}
catch(CustomException e){
...
}

但我遇到了一些代码,它使用catch(CustomException &e)而不是使用catch(CustomException e)。这是a)正确的、b)错误的还是c)灰色地带的?


请注意,正确的引用也是常量:catch(CustomException const &e)... - Alexis Wilke
8
不应标记为重复 -- 通过指针捕获异常与通过引用或值捕获异常是不同的。 - wcochran
紧密相关且有用:为什么要将异常捕获为const引用 - Rick
4个回答

179

C++中处理异常的标准做法是...

抛出值,引用捕获

通过值进行捕获在面对继承层次结构时会带来问题。假设你的例子有另一个类型MyException,它从CustomException继承并覆盖了错误代码等项目。如果抛出了MyException类型,您的catch块会导致其被转换为一个CustomException实例,这将导致错误代码更改。


8
我想补充一点:使用const引用捕获异常。不幸的是,您可以抛出一个const对象,并使用非const引用捕获它。为了避免这种静默的“去除”const,始终使用const引用捕获,并确保您的异常类型具有正确的const访问器。 - Daniel Earwicker
29
为什么使用非 const 引用捕获异常是“不幸的”?异常对象总是被复制,所以无论你抛出什么类型的“对象”,在任何 catch 块中都不能影响原始对象,因为抛出的“对象”只是一个初始化器。没有取消 const 限定符,异常对象始终是 non-const、non-volatile 类型。允许使用非 const 引用捕获异常可以使中间的 catch 块添加关于异常的信息。这种方法不常用,但如果需要,它就存在。 - CB Bailey
2
@John:第15章异常,特别是15.3(异常处理)描述了捕获参数如何从异常对象初始化。如果参数是类类型(而不是引用),则通过复制构造函数进行初始化。 - CB Bailey
5
为了澄清 Charles 的问题(并将答复发送到您的收件箱),你可以捕获并重新抛出的异常对象是临时的。throw语句中的参数应该在堆栈上,并且应该在解除堆栈时被销毁。使用 throw new 是错误的。 - Potatoswatter
4
@Charles - 你说得对,这不是真正的去除const。我想我把它与通过非const引用传递给方法的临时对象视为同一术语,在语言的其他地方被禁止,以避免尝试通过引用来突变临时对象(当突变将被丢弃时)。但正如你所说,你可能希望有意突变异常,然后使用throw;在其他地方使其可见(从而打破我的类比)。 - Daniel Earwicker
显示剩余2条评论

36

如果你使用值捕获方式来处理异常,当捕获到派生类型的异常时,会截取为你所捕获类型的异常对象。

这可能会对catch块中的逻辑造成影响,但没有理由不使用const引用方式进行捕获。

请注意,如果在catch块中使用throw;而没有参数,不管你是捕获了一个被截取过的副本还是一个异常对象的引用,原始异常都将被重新抛出。


8
在这个语境中,我对“slice”一词不太熟悉。 - Keith Pinson
10
我认为“切片”(slice)是那些习惯于引用传递语言的人们喜欢使用的一个术语。这个概念是,如果你将一个派生对象分配给其基类类型,就会“切掉”对象的一部分。但在我看来,这似乎是一种误导性的观点,因为实际上是在基类的复制赋值运算符 base &operator=(const base &other) 中将派生对象隐式转换为 base & 引用,所以赋值运算将派生类视为基类的一样处理,并没有什么神奇的切片或转移。你只是在调用一个函数。 - Injektilo
@Injektilo 我认为这里的slice主要涉及到虚函数。您使用基类引用来捕获派生类异常对象,因此在动态绑定中很有用。对于按值捕获,基类对象将丢失派生类的详细信息,对于按引用捕获,派生类信息仅被隐藏。 - Rick

17

如果您不想处理异常,通常应使用一个const引用:catch (const CustomException& e) { ... }。编译器处理抛出对象的生命周期。


2

(CustomException e) 中创建了 CustomException 的新对象... 因此它的构造函数将被调用,而在 (CustomException &e) 中,只是引用... 不会创建新对象,也不会调用构造函数... 因此前者有点多余... 后者更好使用...


在参考场景中,e的内存如何释放?如果e在堆栈中,catch块就变得毫无意义了吗? - Charlie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接