C++ FAQ Celebrating Twenty-One Years of the C++ FAQ!!!
(Click here for a personal note from Marshall Cline.)
Section 17:
[17.16] How do I throw polymorphically?

Sometimes people write code like:

class MyExceptionBase { };

class MyExceptionDerived : public MyExceptionBase { };

void f(MyExceptionBase& e)
{
  // ...
  throw e;
}

void g()
{
  MyExceptionDerived e;
  try {
    f(e);
  }
  catch (MyExceptionDerived& e) {
    ...code to handle MyExceptionDerived...
  }
  catch (...) {
    ...code to handle other exceptions...
  }
}
If you try this, you might be surprised at run-time when your catch (...) clause is entered, and not your catch (MyExceptionDerived&) clause. This happens because you didn't throw polymorphically. In function f(), the statement throw e; throws an object with the same type as the static type of the expression e. In other words, it throws an instance of MyExceptionBase. The throw statement behaves as-if the thrown object is copied, as opposed to making a "virtual copy".

Fortunately it's relatively easy to correct:

class MyExceptionBase {
public:
  virtual void raise();
};

void MyExceptionBase::raise()
{ throw *this; }

class MyExceptionDerived : public MyExceptionBase {
public:
  virtual void raise();
};

void MyExceptionDerived::raise()
{ throw *this; }

void f(MyExceptionBase& e)
{
  // ...
  e.raise();
}

void g()
{
  MyExceptionDerived e;
  try {
    f(e);
  }
  catch (MyExceptionDerived& e) {
    ...code to handle MyExceptionDerived...
  }
  catch (...) {
    ...code to handle other exceptions...
  }
}
Note that the throw statement has been moved into a virtual function. The statement e.raise() will exhibit polymorphic behavior, since raise() is declared virtual and e was passed by reference. As before, the thrown object will be of the static type of the argument in the throw statement, but within MyExceptionDerived::raise(), that static type is MyExceptionDerived, not MyExceptionBase.