什么是C ++中错误处理的正确方法

时间:2010-06-25 15:19:27

标签: c++ error-handling

一种是使用C ++异常:尝试catch块。但是,当引发异常时,释放动态内存将是一个问题。

其次是使用C风格:errno变量

第三个是在错误时返回-1,在成功时返回0:)

中型项目应选择哪种方式?为什么?还有其他更好的方法..?

8 个答案:

答案 0 :(得分:28)

  

但是,当引发异常时,释放动态内存将是一个问题。

不,不是。 std::vector<int> v(100);完成。

这里的概念称为范围限制资源管理(SBRM),也称为更常见(和笨拙)名称资源获取初始化(RAII)。基本上,所有资源都包含在一些对象中,该对象将清理析构函数中的资源(始终保证为自动分配的对象运行)。因此,无论函数是否正常存在,还是通过异常,都会运行析构函数并清理资源。

永远不要在需要明确释放它的地方进行分配,使用容器和智能指针。

答案 1 :(得分:4)

  

其次是使用C风格:errno变量

     

第三个是在错误时返回-1,在成功时返回0:)

它们如何帮助解决释放动态内存的问题?他们还使用早退策略,与throw相同。

总而言之,它们没有优于C ++异常的优势(根据你的说法)。

答案 2 :(得分:3)

首先,您应该争取最少错误案例的程序。 (因为错误并不酷。)

异常是一个很好的工具,但should be used conservatively:保留它们用于“例外情况”,不要用它们来控制程序的流程。

例如,不要使用异常来测试用户输入是否正确。 (对于这种情况,请返回错误代码。)

答案 3 :(得分:3)

  

一个是使用C ++异常:试试   抓住积木。但释放动态   记忆将成为一个问题   异常被提出。

@see RAII。

异常应该是处理异常运行时情况(例如内存不足)的首选方法。请注意,像std :: map :: find这样的东西不会抛出(并且它不应该),因为搜索不存在的密钥不一定是错误或特殊情况:该函数可以通知客户端是否或不存在密钥。它不像违反前置条件或后置条件,例如要求程序存在以使程序正常运行并发现文件不存在。

异常处理的美妙之处在于,如果你正确地执行它(同样,@ see RAII),它可以避免在整个系统中丢失错误处理代码。

让我们考虑一个函数A调用函数B的情况,函数B调用C然后调用D,依此类推,直到'Z'。 Z是唯一可以抛出的函数,A是唯一对从错误中恢复感兴趣的函数(A是高级操作的入口点,例如像加载图像一样)。如果你坚持使用RAII而不仅仅是异常处理,那么你只需要在Z中放一行代码就可以在A中抛出一个异常和一个try / catch块来捕获异常,比如显示向用户发送错误消息。

不幸的是,很多人并没有像实际应用那样严格遵守RAII,因此很多现实世界的代码都有比处理手动资源清理所需的更多try / catch块(不应该这样做)必须是手动的)。尽管如此,这是您应该努力在代码中实现的理想,如果它是一个中型项目,它更实用。同样,在现实世界的场景中,人们经常忽略函数返回的错误代码。如果你想加倍努力,那么你也可以从RAII开始,因为无论你是否使用异常处理或错误代码处理,这都将对你的应用程序有所帮​​助。

有一点需要注意:您不应该跨模块边界抛出异常。如果这样做,您应该考虑错误代码之间的混合(如返回错误代码,不使用像errno这样的全局错误状态)和异常。

值得注意的是,如果你在代码中使用operator new而没有在任何地方指定nothrow,ex:

int* p = new int(123); // can throw std::bad_alloc
int* p = new(std::nothrow) int(123); // returns a null pointer on failure

...然后你需要在你的代码中捕获并处理bad_alloc异常,以便它能够针对内存不足异常提供强大的功能。

答案 4 :(得分:2)

看看Herb Sutter关于尝试捕获C ++ GOTW的评论。并完成他的整篇文章。关于何时以及如何检查和保存自己的错误条件以及如何以最佳方式处理它们,他确实有很多话要说。

答案 5 :(得分:1)

抛出异常。抛出异常时总会调用变量的析构函数,如果基于堆栈的变量不能自行清理(例如,如果你需要删除结果时使用了原始指针),那么你得到你应得的东西。使用智能指针,没有内存泄漏。

答案 6 :(得分:1)

但是,当引发异常时,释放动态内存将是一个问题。

释放内存(或任何其他资源)不会突然变成非问题,因为您不使用异常。处理这些问题的技术虽然可以轻松抛出异常,但也可以在出现“错误条件”时更容易。

答案 7 :(得分:1)

将控件从一个上下文传递到另一个上下文有例外 您让编译器完成在上下文之间展开堆栈的工作,然后在新上下文中补偿异常(然后希望继续)。

如果您的错误发生并且可以在相同的上下文中纠正,那么错误代码是进行错误处理和清理的好方法(不要认为这意味着您不应该使用RAII,您仍然需要它)。但是例如在一个类中,函数中发生错误,并且调用函数可以纠正该类型的错误(然后它可能不是特殊情况,因此没有异常),然后错误代码是有用的。

当您必须从库或子系统传递信息时,不应使用错误代码,因为您依靠开发人员使用代码实际检查和处理代码以确保其正常工作且比他们不会忽略错误代码。