C++这个C语言运行错误是怎么回事什么意思?

许多入门程序员往往不能正确地认识错误码与异常的异同,导致他们经常会有一些错误的认知,这些认知阻碍了他们进一步了解异常。而异常是C++标准委员会推荐的错误处理机制,不了解异常,很大程度上意味着你并不了解现代C++。

本文旨在向大家阐明,错误码与异常本质上是一样的东西,都是为了处理错误,只不过是具体写法上有所不同。


错误处理与恢复是什么意思

首先,我们先广义地来看问题。我们的问题是什么?我们的问题是,当我们的程序执行到某个函数时,这个函数无法完成它所承诺的功能,即,此函数遇到了一个错误。这个错误导致我们无法完成我们想做的事情。

此时,为了保证我们的程序可以继续正常运行,我们需要从这个错误中恢复。所谓恢复意味着:

  • 将程序恢复到一个可预期的状态

同时,我们需要对这个错误情况进行处理

  • 尝试重试或者使用其他fallback方法
  • 向上报告:如果以上方法都不可取,说明,当前函数并无法对此错误进行处理,我们需要将此错误向上报告,直到有人可以处理此错误

需要注意地是,如果函数f出现了错误,无法完成其所承诺的功能,f的调用者,以及调用者的调用者,一直到某一层调用栈,都会无法完成其所承诺的功能,也即,调用栈的每一层,都需要对此错误进行恢复。但是,如果某一层调用栈,无资源泄漏,也没有破坏程序不变式,错误可以透明地向上报告,不需要额外的恢复动作。

另一方面,当错误传递到某一层函数时,如果此函数的功能并未由于此错误受到影响,或者此错误是此函数预期的,那么,错误传递可以到此为止,我们可以在这一层,将错误消化掉,也即,此错误已经被处理完成。例子如:http服务器的请求循环中,如果某个请求处理失败,请求循环直接返回用户返回一个错误消息即可,如http 404。


当函数遇到一个错误时,我们需要描述此错误,描述此错误的对象,即为错误对象,常见的例子如:

我们会通过这个错误对象来报告我们的错误,并尝试将错误对象向调用者不断传递,直到某个调用者能够处理这个错误。

从错误对象角度来说,错误码与异常对象没有本质的区别,大家可以简单认为,一个错误码,对应一个异常类。错误码用某个值,表示某类错误;异常用某个类,表示某类错误:

异常对象相较于错误码的优点在于:

  • 异常对象可以携带更多的信息,比如,file_not_found_error异常对象,可以携带具体的找不到的文件名,而错误码ENOTFOUND则不能。
  • network_error,然后统一处理。如果想特别处理connection_failed,可以额外catch此异常类。而错误码则需要对所有错误情况一一判断。

异常对象的缺点也是有的,但是对能够认识到异常对象的缺点的大佬,应该已经不需要看我的文章了。


当函数执行遇到错误,使之不能完成承诺的功能时,大多数情况下,我们需要终止此函数的执行,但是终止之前,我们需要保证:

  • 程序恢复到一个正常的状态(因为函数执行过程中,或者已经修改了程序的状态,导致程序处于一个中间状态)

因此,我们通过在遇到错误后,执行一些代码,来让程序从错误中恢复。

为了恢复程序的invariant,通常,出错时我们需要手动处理。

// 恢复程序不变式,并让错误向上报告 // 恢复不变式,并将错误向上报告

其他语言中,经常使用错误处理来管理资源,从而保证错误出现时无资源泄漏。C++中,会使用专门的机制RAII来管理资源,保证资源不会泄漏。因此,在C++中,只要正确使用了RAII,无论使用错误码,还是使用异常处理,都不需要考虑资源泄漏的问题。

// 直接将错误向上传递即可,打开的文件由File_handle管理,会自动释放的,不用担心资源泄漏 // 如果出错了,直接让异常对象自动向上抛就行子,打开的文件会自动释放,不用担心资源泄漏

函数从错误中恢复后,需要决定,是在本地将错误处理掉,还是上报。显然,当函数f()无法完成承诺的任务时,f的调用者g,也是无法完成自己所承诺的任务的,此时,我们需要将错误报告给g。如此重复,调用栈的每一层,都需要进行恢复,直到某一层能够处理此错误(也即,这一层函数遇到错误,其所承诺的功能还是可以正常完成;或者这一层已经是调用栈的顶层了)。

对于错误码,我们需要手动将错误对象向上传递,如果忘记上报,错误就被静默的丢掉了。它可能导致某一层意识不到已经出错,从而无法执行恢复动作,导致程序在一个错误的状态下越走越远。

对于异常,它可以自动上报:

a(); // 如果出错,自动上报 b(); // 如果出错,自动上报

随着错误对象的不断向上报告,调用栈的每一层都需要进行错误恢复。直到我们到达了某一层:

  • 错误可以被忽略(即,此错误的发生,实际上没有影响当前函数完成自己的功能):比如你有一个请求循环,在处理某个具体的请求时出了错,此时,直接catch此错误,然后打条日志。
  • 我们可以尝试其他的方法,来将刚刚失败的工作完成:比如,从网络加载配置失败,收到network_error,则改从本地读取配置文件。
  • 当前函数已经是顶层了,无法继续上报:如main函数、线程的main函数、请求处理循环等。
  • 错误对象需要转换成另外一种错误对象,再继续传播:比如,一个http服务器的项层,某个请求处理错误后,会将异常转换成http错误码,然后返回给用户。

以上四种情况实际上不完全正交。

对错误处理的角度来看,错误码与异常实际上是没有任何区别的。无论你使用错误码,还是使用异常,你都要决定,具体应该怎么处理当前错误对象。

我要回帖

更多关于 C语言运行错误是怎么回事 的文章

 

随机推荐