异常:3.0 版本中对异常宏的修改

这是一个高级主题。

在 MFC 版本 3.0 及更高版本中,异常处理宏已更改为使用 C++ 异常。 本文介绍了这些更改如何影响使用宏的现有代码的行为。

本文涵盖以下主题:

异常类型和 CATCH 宏

在早期版本的 MFC 中,CATCH 宏使用 MFC 运行时类型信息来确定异常的类型;换句话说,异常的类型是在捕获点确定的。 但是,对于 C++ 异常,异常的类型始终在抛出站点由抛出的异常对象的类型确定。 在指向抛出对象的指针的类型与抛出对象的类型不同的极少数情况下,这将导致不兼容。

以下示例说明了 MFC 3.0 版本与早期版本之间这种差异的结果:

TRY
{
   THROW((CException*) new CCustomException());
}
CATCH(CCustomException, e)
{
   TRACE("MFC 2.x will land here\n");
}
AND_CATCH(CException, e)
{
   TRACE("MFC 3.0 will land here\n");
}
END_CATCH

此代码在 3.0 版本中的行为不同,因为控件始终会传递给具有匹配异常声明的第一个 catch 块。 throw 表达式的结果

THROW((CException*) new CCustomException());

作为 CException* 被抛出,即使它被构造为 CCustomException。 MFC 2.5 和更早版本中的 CATCH 宏使用 CObject::IsKindOf 在运行时测试类型。 因为表达式

e->IsKindOf(RUNTIME_CLASS(CException));

为 true,所以第一个 catch 块捕获异常。 在 3.0 版本中,使用 C++ 异常实现许多异常处理宏,第二个 catch 块与引发的 CException 匹配。

此类代码并不常见。 它常见于异常对象传递给另一个接受泛型 CException* 的函数,执行“预抛出”处理并最终抛出异常时。

若要解决此问题,请将 throw 表达式从函数移动到调用代码,并在生成异常时引发编译器已知的实际类型的异常。

重新抛出异常

catch 块无法引发捕获的相同异常指针。

例如,此代码在早期版本中有效,但版本 3.0 将产生意外结果:

TRY
{
   // Do something to throw an exception.
   AfxThrowUserException();
}
CATCH(CException, e)
{
   THROW(e);    // Wrong. Use THROW_LAST() instead
}
END_CATCH
   }

在 catch 块中使用 THROW 会导致删除指针 e 以便外部 catch 站点将收到无效的指针。 使用 THROW_LAST 重新引发 e

有关详细信息,请参阅异常:捕获和删除异常

另请参阅

异常处理