我对在C ++中重新抛出的对象的类型感到很困惑。例如,在上面的代码中,为什么输出241?
我的理解是,在第1行中,抛出了类Bar的对象。它在第2行中被捕获.Bar类型的对象被切成Foo类型。
但是,当重新抛出异常时,它的类型是什么?为什么要执行第3行?它不再是Foo了吗?
重新抛出的基本政策是什么?类型保持不变?或者有什么变化?
#include <iostream>
using namespace std;
class Foo{
public:
Foo(int i): i(i) {}
Foo(){}
int i;
};
class Bar: public Foo{
public:
Bar(int i): Foo(i) {}
Bar(const Bar& b){i=b.i;}
};
int main () {
Bar b(1);
try{
try{
throw b; //Line 1
}
catch(Foo& e){
e.i=2; //Line 2
cout<<e.i;
throw;
}
catch(Bar& e){
e.i = 3;
cout<<e.i;
throw e;
}
}
catch (Bar e) {
e.i*=2; //Line 3
cout<<e.i;
}
catch (Foo e) {
e.i*=3;
cout<<e.i;
}
cout<<b.i;
return 0;
}
答案 0 :(得分:2)
使用throw;
,当前正在处理的异常会向前传播,不会进行复制或切片。 (这里括号中的内容无关紧要:catch (...) {throw;}
将始终重新抛出当前异常,这不一定与您的e
相同。)
如果您将其更改为throw e;
,则e
可能会被复制并切片为Foo
。
答案 1 :(得分:2)
throw;
自己抛出同一个对象。该对象实际上是一个Bar,即使你对它的引用是Foo&amp ;.所以当你说,“它被捕获在第2行.Bar类型的对象被切成Foo类型”,这是不对的。它不是通过捕获或重新抛出来切割的。
如果您将行throw;
更改为throw e;
,那么它将被切片,您将看到261.此外,当您按值捕获时,对象将被复制,因此可能是切片。
最后得到1的原因是对象b永远不会被抛出,并且在任何catch块中都没有被修改。 throw <expression>;
会抛出其操作数的副本。
答案 2 :(得分:1)
答案 3 :(得分:1)
首先,你要说的是在第2行,抛出的对象的类型被切成Foo
。这是不正确的。第2行没有任何内容。您正在通过引用捕获对象,这意味着类型Foo &
的引用附加到Bar
类型的对象。
如果捕获的对象确实是多态的,则可以多态地使用捕获的对象,捕获的对象e
将表现为动态类型Bar
的对象。没有切片发生在那里。
其次,切片是否发生在第2行并不重要。无论throw
子句中使用的参数类型如何,不带参数的catch
总是重新抛出“完整”异常对象。
如果您尝试通过throw e;
重新抛出异常,则确实会发生切片,异常将作为Foo
类型的对象重新抛出。
答案 4 :(得分:1)
Bar b(1);
try
{
try
{
// The object b is copied into the exception area.
// The standard does not define what or where this is
// only that it exist and the exception is copied there.
throw b;
// It is more common to do the following:
throw B(1);
// Though technically the object is constructed then copied
// into the exception area. The compiler can easily remove
// the copy and create the B(1) object directly in the
// exception area.
}
// Note normally you put the most derived types first
// So I would expect the Bar to be caught before the Foo
// as doing it this way hides the Bar catch clause
catch(Foo& e)
{
// Catches the object by reference.
// e is a reference to the exception held in the exception area
// This throws the exception held in the exception area.
throw ;
}
// This will never be called.
// Bar is derived from Foo. And all Foo type objects are caught by
// the previous catch clause.
catch(Bar& e)
{
// Catches the object by reference.
// e is a reference to the exception in the exception area.
// This throws the local variable e as the exception.
// The variable e is copied into the exception area.
//
// Note that if the object was of a type derived from Bar
// then it will be sliced as it is copied into the exception area.
throw e;
}
}
catch (Bar e)
{
// Catches by value.
// Any object of type Bar (or derived from Type Bar) is caught here.
// If the object is of a type derived from Bar then it will be sliced.
}
catch (Foo e)
{
// Catches by value.
// Any object of type Foo (or derived from Type Foo) EXCEPT if it was derived from Bar
// If the object is of a type derived from Foo then it will be sliced.
}