从catch块重新抛出异常时丢失异常类型

时间:2021-12-20 20:31:01

Today I found a bug in a catch block:

今天我在catch块中发现了一个错误:

catch (const exception& e){
    // do something
    // throw e; <-- bug!
    throw;    // <-- right thing to do
}

Basically if I rethrow the exception e explicitly, I get a new std::exception reconstructed, in fact the message from the what() method was the default std::string, instead of my custom built message.

基本上如果我明确地重新抛出异常e,我会重新构造一个新的std :: exception,实际上来自what()方法的消息是默认的std :: string,而不是我的自定义构建消息。

What is the explanation? I thought that throw; is only a shorthand of throw ExceptionJustCaught;.

解释是什么?我以为扔了;只是抛出ExceptionJustCaught;的简写。

2 个解决方案

#1


13  

Exception objects are a bit special. They're constructed in a special place in memory, and their lifetime is determined by the catch block in which they're caught.

异常对象有点特殊。它们是在记忆中的特殊位置构建的,它们的生命周期取决于它们被捕获的捕获块。

If you say throw e;, the lifetime of the original exception ends at the end of the catch block, and you are throwing a new exception by copying e, thus producing a classical slicing problem: Since e is a polymorphic reference to an object whose dynamical type is usually more-derived than std::exception, you end up slicing off the derived part of the object.

如果你说throw e;,原始异常的生命周期在catch块的末尾结束,你通过复制e抛出一个新的异常,从而产生一个经典的切片问题:因为e是对一个对象的多态引用动态类型通常比std :: exception更加派生,你最终会切掉对象的派生部分。

By contrast, throw; is a special statement that reactivates the original exception, so that it is no longer caught, and its lifetime does not end at the end of the block anymore. In fact, if you catch by non-constant reference, you can keep modifying the exception object and rethrow and thus communicate a state change up the lower catch blocks. But bear in mind that rethrowning is different from throwing a new exception!

相比之下,扔;是一个特殊的语句,它重新激活原始异常,因此它不再被捕获,并且它的生命周期不再在块的结尾处结束。实际上,如果您通过非常量引用捕获,则可以继续修改异常对象并重新抛出,从而将更改状态更改为更低的catch块。但请记住,重新抛出与抛出新的异常不同!

#2


8  

Just a throw throws the current exception by reference. throw e copy constructs a new exception to throw. This is much the same way return works.

只是一个throw通过引用抛出当前异常。 throw e copy构造一个新的异常抛出。这与返回工作大致相同。

#1


13  

Exception objects are a bit special. They're constructed in a special place in memory, and their lifetime is determined by the catch block in which they're caught.

异常对象有点特殊。它们是在记忆中的特殊位置构建的,它们的生命周期取决于它们被捕获的捕获块。

If you say throw e;, the lifetime of the original exception ends at the end of the catch block, and you are throwing a new exception by copying e, thus producing a classical slicing problem: Since e is a polymorphic reference to an object whose dynamical type is usually more-derived than std::exception, you end up slicing off the derived part of the object.

如果你说throw e;,原始异常的生命周期在catch块的末尾结束,你通过复制e抛出一个新的异常,从而产生一个经典的切片问题:因为e是对一个对象的多态引用动态类型通常比std :: exception更加派生,你最终会切掉对象的派生部分。

By contrast, throw; is a special statement that reactivates the original exception, so that it is no longer caught, and its lifetime does not end at the end of the block anymore. In fact, if you catch by non-constant reference, you can keep modifying the exception object and rethrow and thus communicate a state change up the lower catch blocks. But bear in mind that rethrowning is different from throwing a new exception!

相比之下,扔;是一个特殊的语句,它重新激活原始异常,因此它不再被捕获,并且它的生命周期不再在块的结尾处结束。实际上,如果您通过非常量引用捕获,则可以继续修改异常对象并重新抛出,从而将更改状态更改为更低的catch块。但请记住,重新抛出与抛出新的异常不同!

#2


8  

Just a throw throws the current exception by reference. throw e copy constructs a new exception to throw. This is much the same way return works.

只是一个throw通过引用抛出当前异常。 throw e copy构造一个新的异常抛出。这与返回工作大致相同。