请解除我的异常处理问题

时间:2011-07-08 02:24:11

标签: c++ exception

许多程序员喜欢使用异常来响应程序中的异常情况(如运行时错误)。为了更清楚地说明我的问题,我发布了两个代码片段,如下所示。我的问题是“尝试... catch块比程序员自己在错误检查和处理方面更安全,更强大,更智能吗?”我想要你的善意。提前谢谢!

  1. 使用try ... catch block

    std::map<std::string, Array2D<unsigned short>* > DataPool;
    
    try
    {  
       for (int i = 0; i < 4; i++)
       {   
           std::stringstream ss;
           ss << "I" << i;
           std::string sKey = ss.str();
    
           // Insert an element into the map container
           DataPool.insert(std::make_pair(sKey, new Array2D<unsigned short>(2288, 2288))); 
       }
    }
    catch (bad_alloc&)
    {  
        cout << "Error allocating memory." << endl;
    
        // Free allocated memory
        for (std::map<std::string, Array2D<unsigned short>* >::iterator it = DataPool.begin(); it != DataPool.end(); it++)
           delete ( *it ).second;
    
       DataPool.clear();
    }
    
  2. 自己处理错误

    std::map<std::string, Array2D<unsigned short>* > DataPool;
    Array2D<unsigned short>* pBuffer = NULL;    
    
    for (int i = 0; i < 4; i++)
    {   
        std::stringstream ss;
        ss << "I" << i;
        std::string sKey = ss.str();
    
        pBuffer = new Array2D<unsigned short>(2288, 2288);
    
        if (pBuffer == NULL)
        {  
           // Free allocated memory
           for (std::map<std::string, Array2D<unsigned short>* >::iterator it = DataPool.begin(); it != DataPool.end(); it++)
               delete ( *it ).second;
    
           DataPool.clear();
           break;
        }
    
        // Insert an element into the map container
        DataPool.insert(std::make_pair(sKey, pBuffer)); 
    }
    

4 个答案:

答案 0 :(得分:3)

至少对于上面的代码片段,答案很简单:第一个是“更安全”(尽管存在其他代码问题),因为第二个代码片段没有按照您的想法执行。

第二个中的错误检查将不起作用,因为new在合理符合标准的C ++编译器中永远不会返回null。当分配失败时,C ++标准要求new 以抛出bad_alloc。因此,处理bad_alloc抛出的new次异常是唯一方式来检查new失败

请注意,我正在谈论C ++标准以及您在问题中的代码片段。 C ++编译器实现可能具有偏离此行为的选项,这些选项应该(应该)完整记录。正如Ben Voigt所提到的,std::nothrow可以用new代替NULL

答案 1 :(得分:3)

这些都不推荐。

了解RAII.了解it provides exception safety much better than try/catch/finally的方式。然后使用它。

通过所有更好的措施(更安全,更强大/更灵活,更智能),RAII优于try / catch

答案 2 :(得分:2)

您的第一个问题是您没有使用RAII。 RAII不仅是C ++中正确资源处理的基础,它几乎也是编写异常安全代码所必需的。

您的小例子并没有真正显示异常处理的优点,正是因为它非常小,不需要异常提供的非本地控制流。在较大的程序中,导致问题的代码和知道如何对问题做出反应的代码彼此之间的距离很远,这种情况很常见,并且异常提供了一种干净的通用方法来报告执行代码的问题。堆。在某些情况下,异常对于与发生错误无关的其他控制流也很有用,但是可以保证从一段代码中退出。

使用RAII,您的示例将如下所示:

std::map<std::string, std::unique_ptr<Array2D<unsigned short> > > DataPool;
for (int i(0); i < 4; ++i) {
    std::stringstream ss;
    ss << "I" << i;

    std::string key(ss.str());
    std::unique_ptr<Array2D<unsigned short> > value(
        new Array2D<unsigned short>(2288, 2288));

    DataPool.insert(std::make_pair(key, value));
}

请注意,没有任何手动清理,因为它全部由RAII的魔力管理。需要清理的所有资源都放入对象中,这些对象在不能抛出的操作中自动管理范围退出时的清理。这意味着当退出范围时,可以保证清理这些资源。我在此示例中使用std::unique_ptr是为了简洁,但如果您的系统尚不支持它,那么您还可以使用包含std::map的类,并确保随后删除所有插入的值。

"Error allocating memory."消息在这部分代码中不太可能有用,但调用代码可能对正确的操作有一些了解。这是因为调用代码具有更多关于代码试图解决的实际问题的上下文,而不是这个小片段,应该编写为可在各种不同情况下使用。在各种不同的情况下有用表明它应该尝试做它所需要的,并简单地向调用代码报告它所做的任何问题(而不是打印到某些流,这可能是完全不合适的)

返回代码还可以允许代码向其调用者报告问题,但是使用返回代码会阻止从函数返回值,并且与C ++中的构造模型不兼容(C ++中的构造函数必须创建工作对象或抛出例外)。返回代码也可能导致非常难以理解的代码,其中大部分代码与错误处理有关,而不是代码试图解决的实际问题。返回代码的另一个问题是,如果异常抛出路径很少发生,它们的效率可能低于异常(警告lector - 不要理解我的话!如果你担心性能那么你应该测量代码的实际性能。)

答案 3 :(得分:-1)

尝试... catch会让你的代码运行得更慢。最好先手动进行错误处理,以便控制可能发生的每个错误方面(并决定如何处理错误)

在C#中,通常所有顶级函数通常都包含在try ... catch中,但这必须首先与错误处理结合使用。