views:

154

answers:

6

Here is main():

int main()
{
    B b(1,"two","three");
    try
    {
        f1(b);
    }
    catch(B& b_ref)
    {
        cout<<"Caught B&"<<endl;
        b_ref.print();
    }
    catch(A& a_ref)
    {
        cout<<"Caught A&"<<endl;
        a_ref.print();
    }

    system("pause");
    return 0;
}

Here is f1():

void f1(A& subject)
{
    throw subject;
}

Information:

B inherits from A. A::print() is virtual, and is reimplemented in B. The catch that catches the exception is catch(A& a_ref), which I guess makes sense, since the exceptions' static type (subject) is A&. BUT, why isn't B:: print() running? Is the dynamic type "lost"? Only A::print() runs in the line a_ref.print();.

Can somebody explain?

+5  A: 

When you say "throw subject" a new exception object is created based on the static type of the throw expression (subject). The fact that subject is a reference is irrelevant for the purposes of determining what object to throw. A new A object is copy constructed from subject. This copy (or possibly a copy of this copy) is the actual object that is caught.

Charles Bailey
+6  A: 

throw only throws an object of the type of the expression which follows it. In this case, subject is of type A&, regardless of what the actual object is, so an A is thrown (note that references cannot be thrown, so a copy is made).

You can deal with this by adding a member function to your exception classes which throws the exception. As long as you implement this method in every class, the override which gets called will know the runtime type of the object and can throw *this.

http://www.ddj.com/cpp/184401940

Steve Jessop
Exactly, yes. It works with pointers because the pointer itself is the object thrown, whereas when you throw using a reference it's the referand which is thrown (and hence copied, and hence sliced).
Steve Jessop
Please note that you have to be very careful when throwing pointers. If you throw pointers to dynamically created objects it can be extremely hard to get the ownership semantics consistent. Throwing pointers to local objects usually incorrect and throwing pointers to global objects... well, implies you have global objects.
Charles Bailey
I'm aware of that pitfall. However, if the pointer was created in main(), say, and not dynamically (not with 'new'), there isn't any risk, is there?
A: 

Because you catch by reference the object is 'sliced'.

If you really want polymorhpic behaviour, try using something like the pimpl idiom.

Visage
Not quite true; it is catching by value, not reference, that causes the 'slicing' problem, and that's not what's going on here. It is rather that 'throw' doesn't consider the actual type of the object, just the static type. (see onebyone's answer).
Nick Meyer
A: 

nvm, my lack reading comprehension skills. Thought he's creating objects of types A.

Ray
This is backwards. If print() is virtual and you have an A pointer or reference to what is actually a B, then B::print gets called. A::print only gets called if B::print explicitly calls the base class.
Nick Meyer
+4  A: 

Catch blocks work polymorphically, but throw doesn't. When you say:

void f1(A& subject)
{
    throw subject;
}

you are throwing an A, although the thing passed to the function is a B.

anon
That's nasty (slicing problem will apply). Learn something new every day.
Martin York
+5  A: 

According to C++ standard 15.1/3:

A throw-expression initializes a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw...

So you create temporary object of type A, not B.

Kirill V. Lyadvinsky
+1 for citing the Standard
Bklyn