What object is rethrown in C++?
I am quite confused about the type of the object which is rethrown in C++. For example, in the code above, why is the output 241?
My understanding is that in Line 1, an object of class Bar is thrown. It is caught in Line 2. The object of type Bar is sliced to type of Foo.
However, when the exception is rethrown, what's the type of that? Why Line 3 is executed? It's not Foo any more?
What's the basic policy of rethrow? The type remains the same? Or anything changed?
#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开发者_如何学Python (Foo e) {
e.i*=3;
cout<<e.i;
}
cout<<b.i;
return 0;
}
throw;
on its own throws the same object. The object is really a Bar, even though your reference to it is a Foo&. So when you say, "It is caught in Line 2. The object of type Bar is sliced to type of Foo", that's not right. It's not sliced either by the catch or by the rethrow.
If you change the line throw;
to throw e;
, then it will be sliced, and you'll see 261. Also, when you catch by value the object is copied, and so potentially can be sliced.
The reason you get 1 at the end is that the object b is never thrown, and is not modified in any of the catch blocks. throw <expression>;
throws a copy of its operand.
With throw;
the exception currently being handled is propagated onwards, with no copying or slicing. (It doesn't matter what is in the parentheses here: catch (...) {throw;}
will always rethrow the current exception, which is not necessarily the same as your e
.)
If you changed it to throw e;
, e
may be copied and sliced to Foo
.
In C++, when you write:
throw;
Within a catch block, it "will re throw what ever was caught". This will be the same instance (Bar b) in all cases.
Firstly, you are saying that at line 2 the thrown object's type is sliced to Foo
. This is incorrect. Nothing is sliced at line 2. You are catching the object by reference, which means that the reference of type Foo &
is attached to an object of type Bar
.
You can use the caught object polymorphically, if it is really polymorphic, and the caught object e
will behave as object of dynamic type Bar
. No slicing takes place there whatsoever.
Secondly, it doesn't really matter whether slicing occurs at line 2 or not. A throw
without parameters always rethrows the "full" exception object, regardless of the parameter type used in the catch
clause.
If you tried to rethrow the exception by doing throw e;
the slicing would indeed occur and the exception would be rethrown as an object of type Foo
.
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.
}
精彩评论