本文介绍发生异常时释放对象的需求和方法。 主题包括:
框架或应用程序引发的异常会中断正常的程序流。 因此,请务必密切跟踪对象,以便在引发异常时正确处理它们。
有两个主要方法可以执行此作。
使用
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 函数可能会引发异常。
有关详细信息,请参阅 异常:捕获和删除异常。