异常:释放异常中的对象
本文介绍发生异常时释放对象的必要性和方法。 主题包括:
框架或应用程序引发的异常会中断正常的程序流。 因此,请务必密切跟踪对象,这样才能在引发异常时正确处理它们。
可通过两种主要方法完成此操作。
使用
try
和catch
关键字在本地处理异常,然后使用一个语句销毁所有对象。销毁
catch
块中的所有对象,然后在块外部引发异常以进行进一步处理。
下面将这两种方法作为以下问题示例的解决方案进行说明:
void SomeFunc() // Problematic code
{
CPerson* myPerson = new CPerson;
// Do something that might throw an exception.
myPerson->SomeFunc();
// Now destroy the object before exiting.
// If SomeFunc above throws an exception this code will
// not be reached and myPerson will not be deleted.
delete myPerson;
}
如上所述,如果 SomeFunc
引发异常,则不会删除 myPerson
。 执行直接跳转到下一个外部异常处理程序,绕过正常函数出口和删除对象的代码。 当异常离开函数时,指向对象的指针超出范围,只要程序一直运行,该对象占用的内存将永远不会恢复。 这是内存泄漏,可通过内存诊断进行检测。
在本地处理异常
try/catch 范例提供了一种防御性编程方法,用于避免内存泄漏并确保在发生异常时销毁对象。 例如,本文前面所示的示例可以按如下所示重写:
void SomeFunc()
{
CPerson* myPerson = new CPerson;
try
{
// Do something that might throw an exception.
myPerson->SomeFunc();
}
catch (CException* e)
{
// Handle the exception locally
e->Delete();
}
// Now destroy the object before exiting.
delete myPerson;
}
此新示例设置异常处理程序以捕获异常并在本地进行处理。 然后它正常退出函数并销毁对象。 此示例的重要方面是,使用 try/catch 块建立捕获异常的上下文。 如果没有本地异常框架,该函数永远不会知道异常已引发,也不会有机会正常退出并销毁对象。
销毁对象后引发异常
处理异常的另一种方法是将它们传递给下一个外部异常处理上下文。 在 catch
块中,可对本地分配的对象执行一些清理,然后对其引发异常以进行进一步处理。
引发函数可能需要也可能不需要释放堆对象。 如果函数在恢复正常情况之前始终释放堆对象,那么函数还应在引发异常之前释放堆对象。 另一方面,如果函数在恢复正常情况之前通常不释放对象,那么你必须根据具体情况决定是否应释放堆对象。
以下示例演示如何清理本地分配的对象:
void SomeFunc()
{
CPerson* myPerson = new CPerson;
try
{
// Do something that might throw an exception.
myPerson->SomeFunc();
}
catch (CException* e)
{
e->ReportError();
// Destroy the object before passing exception on.
delete myPerson;
// Throw the exception to the next handler.
throw;
}
// On normal exits, destroy the object.
delete myPerson;
}
异常机制自动释放框架对象;还会调用框架对象的析构函数。
如果调用可能会引发异常的函数,则可使用 try/catch 块来确保捕获异常并有机会销毁已创建的任何对象。 尤其要注意的是,许多 MFC 函数都可能引发异常。
有关详细信息,请参阅异常:捕获和删除异常。