异常:释放异常中的对象

本文介绍发生异常时释放对象的需求和方法。 主题包括:

框架或应用程序引发的异常会中断正常的程序流。 因此,请务必密切跟踪对象,以便在引发异常时正确处理它们。

有两个主要方法可以执行此作。

  • 使用 trycatch 关键字在本地处理异常,然后使用一个语句销毁所有对象。

  • 在阻止外部引发异常以进一步处理之前,销毁块中的任何 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 函数可能会引发异常。

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

另请参阅

异常处理