异常:释放异常中的对象

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

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

可通过两种主要方法完成此操作。

  • 使用 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 函数都可能引发异常。

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

另请参阅

异常处理