Whats the right approach for error handling in C++
一种是使用C ++异常:尝试catch块。 但是,当引发异常时,释放动态内存将成为一个问题。
第二种是使用C风格:errno变量
第三个是在错误时返回-1,在成功时返回0 :)
应该选择哪种方式进行中型项目?为什么? 还有其他更好的方法..?
But freeing dynamic memory will be an issue when an exception is raised.
不,这不对。
这里的概念称为范围限制资源管理(SBRM),也称为更常见(和笨拙)名称资源获取初始化(RAII)。基本上,所有资源都包含在一些对象中,该对象将清理析构函数中的资源(始终保证为自动分配的对象运行)。因此,无论函数是正常存在还是通过异常存在,都会运行析构函数并清理您的资源。
永远不要在需要明确释放它的地方进行分配,使用容器和智能指针。
Second is to use C style: errno variable
Third is just to return -1 on error and 0 on success :)
它们如何帮助解决释放动态内存的问题?他们还使用早退策略,与
总而言之,它们没有优于C ++异常的优势(根据你的说法)。
One is to use C++ exceptions: try
catch blocks. But freeing dynamic
memory will be an issue when an
exception is raised.
@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:
1 2 | 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异常,以便对内存不足异常具有强大的功能。
首先,您应该努力寻找一个具有最小错误案例的程序。 (因为错误并不酷。)
例外是一个很好的工具,但应该保守地使用:保留它们用于"例外情况",不要用它们来控制程序的流程。
例如,不要使用异常来测试用户输入是否正确。 (对于这种情况,请返回错误代码。)
看看Herb Sutter对C ++ GOTW试试catch的评论。并完成他的整篇文章。关于何时以及如何检查和保存自己的错误条件以及如何以最佳方式处理它们,他确实有很多话题。
将控制权从一个上下文传递到另一个上下文有例外。
您让编译器完成在上下文之间展开堆栈的工作,然后在新上下文中补偿异常(然后希望继续)。
如果您的错误发生并且可以在相同的上下文中纠正,那么错误代码是进行错误处理和清理的好方法(不要认为这意味着您不应该使用RAII,您仍然需要它)。但是例如在一个类中,函数中发生错误,并且调用函数可以纠正该类型的错误(然后它可能不是特殊情况,因此没有异常),然后错误代码是有用的。
当您必须从库或子系统传递信息时,您不应该使用错误代码,因为您依靠开发人员使用代码实际检查和处理代码以确保它正常工作并且更经常地工作忽略错误代码。
但是,当引发异常时,释放动态内存将成为一个问题。
释放内存(或任何其他资源)不会突然变成非问题,因为您不使用异常。处理这些问题的技术虽然可以简单地抛出异常,但也可以在出现"错误条件"时更容易。
抛出一个例外。抛出异常时总会调用变量的析构函数,如果基于堆栈的变量不能自行清理(例如,如果你需要删除结果时使用了原始指针),那么你得到你应得的东西。使用智能指针,没有内存泄漏。