none
对MFC的抛出异常机制的疑惑 RRS feed

  • 问题

  • 在公司闲得无事,看看《More effective C++》,看到规则十二:
    条款十二:理解“抛出一个异常”与“传递一个参数”或“调用一个虚函数”间的差异

      下面是我的一些理解:
    我认为

    try 
    
     {
    
    
    
      throw(para)
    
    }
    
    
    
    catch( para)
    
    {
    
    
    
    }
    
    
    
    

         这里throw和catch两个动作的参数的类型应该是一致的。为此作者也专门把这一动作和函数的参数传递作比较,当然如作者所言,二者还是有根本区别的:比如抛出异常不可能回到调用者那儿,而函数参数传递是必然要回到调用者那儿的。

    为此我设想了三种抛出异常的情形:

    第一种拷贝对象,再传递对象:

      

    Class Object; // Object是一个类
    
    
    
    try 
    
     {
    
    Object object1; // 定义一个Object类的一个对象object1
    
    ……
    
      throw(object1)
    
    }
    
    
    
    catch(Object obj)
    
    {
    
    
    
    }
    
    
    
    
    
    

      这里把这段代码细化一下,我感觉画一个流程图大家看得更清楚些:

     

         这里实际上类的构造函数调用了两次,所以作者在文中说:当抛出一个异常时,系统构造的(以后会析构掉)被抛出对象的拷贝数比以相同对象做为参数传递给函数时构造的拷贝数要多一个。

    第二种拷贝对象,传递引用
       

      其对应的流程图如下:

    你可以看到这里,类的构造函数只调用了一次。

    第三种办法,在堆上创建对象,复制指针,传递指针

    Class Object; // Object是一个类
    
    
    
    try 
    
     {
    
    Object *pObject1; // 定义一个Object类的一个对象object1
    
    ……
    
    pObject1 = new Object();
    
      throw(pObject1)
    
    }
    
    
    
    catch(Object *pObj)
    
    {
    
    
    
    }
    
    
    
    
    
    

    其对应的流程图如下:

     这里要详细说说为什么要new一个对象出来?因为文中作者说:我们还没有讨论通过指针抛出异常的情况,不过通过指针抛出异常与通过指针传递参数是相同的。不论哪种方法都是一个指针的拷贝被传递。你不能认为抛出的指针是一个指向局部对象的 指针,因为当异常离开局部变量的生存空间时,该局部变量已经被释放。Catch子句将获得一个指向已经不存在的对象的指针。这种行为在设计时应该予以避免。

    现在我们比较这三种做法,主要是从效率上比较,第一种做法毫无疑问是最低的,因为两次两次调用了拷贝构造函数。我们这种比较第二种做法和第三种做法:从效率而言,第二种做法和第三种做法应该没有区别,因为都调用了一次拷贝构造函数。但第三种做法有一种严重弊端,就是它的异常对象类的内存是在堆上的,因此它必须手动释放才能避免内存泄露。照这样说,综合来说,第二种做法是最好的。

      但今天我看了微软的MSDN上的一个例子(MSDN Library for Visual Studio 2008 SP1 简体中文):

    try
    
    {
    
     CFile file(_T("\\this_file_should_not_exist.dat"), CFile::modeRead);
    
    }
    
    catch( CFileException* theException )
    
    {
    
     if( theException->m_cause == CFileException::fileNotFound )
    
      TRACE( "File not found\n" );
    
     theException->Delete();
    
    }
    
    
    
    
    我又感到疑惑了,看代码微软的做法是通过在堆上构造异常对象,然后将指针传递给catch子句的。其中 theException->Delete();这一句实际上是调用CException::Delete函数的。MSDN对 CException::Delete函数的解释是:
    This function checks to see if the CException object was created on the heap, and if so, it calls the delete operator on the object.

      我不明白为何微软这样设计呢?

     

     

     

     

     

     

     

     

     

     

    
    

    前无古人,后无来者
    • 已编辑 clever101 2010年6月8日 16:36 出错
    2010年6月8日 16:34

答案

  • 分析得很正确,对于异常处理可以看Exceptional C++中,介绍的比EC++详细。

    这是历史遗留问题,在MFC在C++标准支持异常处理之前就开始使用异常机制。

    另外对于MFC使用指针,MFC提供了一套TRY, CATCH, ENDCATCH的宏可以自动释放,

    http://msdn.microsoft.com/it-it/library/sas5wzs9(VS.80).aspx


    麻烦把正确答案设为解答。
    • 已标记为答案 Kira Qian 2010年6月15日 5:34
    2010年6月9日 1:59
    版主
  • C++中的try和catch是不被提倡使用特性,所以一般工程开发都不用这两个关键字.

    微软本来的代码使用C开发的,不存在try和catch.


    0xBAADF00D
    • 已标记为答案 Kira Qian 2010年6月15日 5:34
    2010年6月10日 3:57
    版主

全部回复

  • 分析得很正确,对于异常处理可以看Exceptional C++中,介绍的比EC++详细。

    这是历史遗留问题,在MFC在C++标准支持异常处理之前就开始使用异常机制。

    另外对于MFC使用指针,MFC提供了一套TRY, CATCH, ENDCATCH的宏可以自动释放,

    http://msdn.microsoft.com/it-it/library/sas5wzs9(VS.80).aspx


    麻烦把正确答案设为解答。
    • 已标记为答案 Kira Qian 2010年6月15日 5:34
    2010年6月9日 1:59
    版主
  • C++中的try和catch是不被提倡使用特性,所以一般工程开发都不用这两个关键字.

    微软本来的代码使用C开发的,不存在try和catch.


    0xBAADF00D
    • 已标记为答案 Kira Qian 2010年6月15日 5:34
    2010年6月10日 3:57
    版主