什么时候可以通过捕获异常来真正解决问题?
When can the problem actually be fixed by catching an exception?
事情是这样的。关于异常,我有一些不太了解的地方,对我来说,它们似乎是一种 几乎 有效但不能干净利落地使用的结构。
我有一个简单的问题。什么时候捕获异常是解决问题根本原因的有用或必要的组成部分? IE。您什么时候能够编写代码来解决通过异常发出的问题?我正在寻找事实数据,或者你有过的经验。
这就是我的意思。一个正常的程序确实有效。如果某项工作由于 X 原因无法完成,则负责执行该工作的函数将抛出异常。但是谁捕捉到异常呢?如我所见,您可能希望捕获异常的三个原因:
- 你抓住它是因为你想改变它的类型并重新抛出它。 (当您将机械异常(例如
std::out_of_range
)转换为业务异常(例如 could_not_complete_transaction
)时会发生这种情况)
- 你抓住它是因为你想记录它,或者在中止之前让用户知道这个问题。
- 你抓住它是因为你真的知道如何解决问题。
我对第 3 点持怀疑态度。我从来没有真正捕获过知道如何解决它的异常。当你得到 std::out_of_memory
时,你应该用它做什么?这不像您可以交换操作系统 来获得更多内存。那不是您可以解决的问题。不仅 std::out_of_memory
,还有业务 class 异常也受此影响。考虑一个潜在的 connection_error
异常:除了等待并稍后重试并希望它自行修复外,你能做些什么来解决这个问题?
现在,公平地说,我确实知道一种情况,在这种情况下,代码会捕获异常并尝试修复问题。我知道某些 Win32 SEH 处理程序会捕获堆栈溢出异常,并在可能的情况下尝试通过扩大线程堆栈的大小来解决问题。但是,这是有效的,因为 SEH 具有尝试恢复语义,而 C++ 异常没有(您无法在异常发生时恢复)。
问题的主要部分已经结束。 但是,我还有一个例外问题,对我来说,这似乎正是你没有 catch 的原因解决问题的子句:捕获异常的代码必须与抛出异常的代码相结合。因为,为了解决问题,它必须特定领域了解问题的原因。但是当一些库文档说“如果这个函数失败,将抛出一个 internal_error
异常”,而我不知道库内部是如何工作的,我应该如何解决这个问题?
PS:请注意,这不是“异常与错误代码”之类的问题;我很清楚错误代码作为一种错误处理机制很糟糕。他们实际上遇到了我为例外情况解释过的相同问题。
这是我的看法,
捕获异常的原因更多,例如,如果它是一个关键应用程序,例如在变电站等中发现的异常,并且捕获了一个没有已知系统恢复或解决方案的异常,您可能希望有一个受控的关机,保护某些模块,保护连接的嵌入式系统等,而不是让系统自行崩溃。后者可能是灾难性的...
I.e. when have you been able to write code that fixes a problem signaled through an exception?
When you get a std::out_of_memory, what are you supposed to do with it? It's not like you can barter the operating system to get more memory.
实际上,我觉得那是我有一段时间的主要编码风格。举个例子:我工作的一个系统没有大量内存,而且系统是专用的,所以,它只是我的应用程序,没有别的。每当我遇到 out_of_memory 类型的异常时,我都会终止较旧的进程并打开具有更高优先级的进程。当然,我会等待杀戮以可控的方式发生。
Think about a potential connection_error exception: what can you do to fix this except wait and retry later and hope it fixes itself?
我会尝试通过蓝牙、光纤、总线等其他媒介进行连接。通常情况下当然会有主要的联系媒介,除非有例外,否则不会调用其他媒介。
But when some library documents that "if this function fails, an internal_error exception will be thrown", how am I supposed to be able to fix the problem when I don't know how the library works internally?
大多数情况下,专用库中的异常在您的系统中会产生与其自身不同的结果。您可能不需要阅读库及其内部工作原理来解决问题。您只需要研究它对您的软件的影响并处理这种情况。这可能是最简单的解决方案。如果库引发已知异常而不是仅仅崩溃或给出乱码答案,那么这就容易多了。
我认为你的问题是你把“解决问题”等同于“让程序继续正确运行”。这是错误的思考异常或一般错误处理的方式。
任何类型的错误处理代码都不应该是程序内部可修复的代码。也就是说,由于 编程 错误,不应输入错误处理逻辑(如捕获异常)。
如果用户给你一个不存在的文件名,那不是编程错误;这是一个用户错误。您不能在不返回给用户并获取现有文件的情况下“修复”该问题。但是异常确实允许您撤消您试图做的事情,将程序恢复到有效状态,然后将发生的事情传达给用户。
invalid_connection
同样不是编程错误。与上述不同,它也不一定是用户错误。这是预期能够发生的事情,不同的程序将以不同的方式处理它。有些人会想再试一次。其他人会想要停止并让用户知道。
关键是,因为没有办法处理这种情况,图书馆也做不到。必须将错误提供给库的调用者以弄清楚要做什么。
如果您有一个解析整数的函数,并且给定的文本不符合整数,那么 函数的工作不是确定下一步该做什么.需要通知调用者他们提供的字符串格式不正确,应该采取措施。
调用方需要处理错误
您不会中止大多数程序,因为本应包含整数的文件实际上并不包含整数。但是你的解析函数确实需要将这个事实传达给调用者,而调用者确实需要处理这种可能性。
这就是“捕获异常”的目的。
现在,像 OOM 这样的意外环境条件是另一回事了。这通常不是外部代码的错误,但通常也不是编程错误。如果它 是 一个编程错误(即:内存泄漏),那么在大多数情况下这不是您可以处理的。 P0709 有一整节关于程序能够(或缺乏)一般响应 OOM 的能力。结果是,即使程序针对 OOM 异常进行了防御性编码,当它们 运行 内存不足时,它们通常 仍然会损坏 。
尤其是在处理 OS 的页面时,这些页面在您实际使用它们之前不会将页面提交到内存中。
事情是这样的。关于异常,我有一些不太了解的地方,对我来说,它们似乎是一种 几乎 有效但不能干净利落地使用的结构。
我有一个简单的问题。什么时候捕获异常是解决问题根本原因的有用或必要的组成部分? IE。您什么时候能够编写代码来解决通过异常发出的问题?我正在寻找事实数据,或者你有过的经验。
这就是我的意思。一个正常的程序确实有效。如果某项工作由于 X 原因无法完成,则负责执行该工作的函数将抛出异常。但是谁捕捉到异常呢?如我所见,您可能希望捕获异常的三个原因:
- 你抓住它是因为你想改变它的类型并重新抛出它。 (当您将机械异常(例如
std::out_of_range
)转换为业务异常(例如could_not_complete_transaction
)时会发生这种情况) - 你抓住它是因为你想记录它,或者在中止之前让用户知道这个问题。
- 你抓住它是因为你真的知道如何解决问题。
我对第 3 点持怀疑态度。我从来没有真正捕获过知道如何解决它的异常。当你得到 std::out_of_memory
时,你应该用它做什么?这不像您可以交换操作系统 来获得更多内存。那不是您可以解决的问题。不仅 std::out_of_memory
,还有业务 class 异常也受此影响。考虑一个潜在的 connection_error
异常:除了等待并稍后重试并希望它自行修复外,你能做些什么来解决这个问题?
现在,公平地说,我确实知道一种情况,在这种情况下,代码会捕获异常并尝试修复问题。我知道某些 Win32 SEH 处理程序会捕获堆栈溢出异常,并在可能的情况下尝试通过扩大线程堆栈的大小来解决问题。但是,这是有效的,因为 SEH 具有尝试恢复语义,而 C++ 异常没有(您无法在异常发生时恢复)。
问题的主要部分已经结束。 但是,我还有一个例外问题,对我来说,这似乎正是你没有 catch 的原因解决问题的子句:捕获异常的代码必须与抛出异常的代码相结合。因为,为了解决问题,它必须特定领域了解问题的原因。但是当一些库文档说“如果这个函数失败,将抛出一个 internal_error
异常”,而我不知道库内部是如何工作的,我应该如何解决这个问题?
PS:请注意,这不是“异常与错误代码”之类的问题;我很清楚错误代码作为一种错误处理机制很糟糕。他们实际上遇到了我为例外情况解释过的相同问题。
这是我的看法,
捕获异常的原因更多,例如,如果它是一个关键应用程序,例如在变电站等中发现的异常,并且捕获了一个没有已知系统恢复或解决方案的异常,您可能希望有一个受控的关机,保护某些模块,保护连接的嵌入式系统等,而不是让系统自行崩溃。后者可能是灾难性的...
I.e. when have you been able to write code that fixes a problem signaled through an exception? When you get a std::out_of_memory, what are you supposed to do with it? It's not like you can barter the operating system to get more memory.
实际上,我觉得那是我有一段时间的主要编码风格。举个例子:我工作的一个系统没有大量内存,而且系统是专用的,所以,它只是我的应用程序,没有别的。每当我遇到 out_of_memory 类型的异常时,我都会终止较旧的进程并打开具有更高优先级的进程。当然,我会等待杀戮以可控的方式发生。
Think about a potential connection_error exception: what can you do to fix this except wait and retry later and hope it fixes itself?
我会尝试通过蓝牙、光纤、总线等其他媒介进行连接。通常情况下当然会有主要的联系媒介,除非有例外,否则不会调用其他媒介。
But when some library documents that "if this function fails, an internal_error exception will be thrown", how am I supposed to be able to fix the problem when I don't know how the library works internally?
大多数情况下,专用库中的异常在您的系统中会产生与其自身不同的结果。您可能不需要阅读库及其内部工作原理来解决问题。您只需要研究它对您的软件的影响并处理这种情况。这可能是最简单的解决方案。如果库引发已知异常而不是仅仅崩溃或给出乱码答案,那么这就容易多了。
我认为你的问题是你把“解决问题”等同于“让程序继续正确运行”。这是错误的思考异常或一般错误处理的方式。
任何类型的错误处理代码都不应该是程序内部可修复的代码。也就是说,由于 编程 错误,不应输入错误处理逻辑(如捕获异常)。
如果用户给你一个不存在的文件名,那不是编程错误;这是一个用户错误。您不能在不返回给用户并获取现有文件的情况下“修复”该问题。但是异常确实允许您撤消您试图做的事情,将程序恢复到有效状态,然后将发生的事情传达给用户。
invalid_connection
同样不是编程错误。与上述不同,它也不一定是用户错误。这是预期能够发生的事情,不同的程序将以不同的方式处理它。有些人会想再试一次。其他人会想要停止并让用户知道。
关键是,因为没有办法处理这种情况,图书馆也做不到。必须将错误提供给库的调用者以弄清楚要做什么。
如果您有一个解析整数的函数,并且给定的文本不符合整数,那么 函数的工作不是确定下一步该做什么.需要通知调用者他们提供的字符串格式不正确,应该采取措施。
调用方需要处理错误
您不会中止大多数程序,因为本应包含整数的文件实际上并不包含整数。但是你的解析函数确实需要将这个事实传达给调用者,而调用者确实需要处理这种可能性。
这就是“捕获异常”的目的。
现在,像 OOM 这样的意外环境条件是另一回事了。这通常不是外部代码的错误,但通常也不是编程错误。如果它 是 一个编程错误(即:内存泄漏),那么在大多数情况下这不是您可以处理的。 P0709 有一整节关于程序能够(或缺乏)一般响应 OOM 的能力。结果是,即使程序针对 OOM 异常进行了防御性编码,当它们 运行 内存不足时,它们通常 仍然会损坏 。
尤其是在处理 OS 的页面时,这些页面在您实际使用它们之前不会将页面提交到内存中。