我应该在现有代码库中添加异常处理吗?

问题描述 投票:4回答:3

我想知道将异常处理添加到现有代码的优缺点。

我在一个控制Windows环境中的h / w卡的SDK上工作。

SDK由100多个相互交互的DLL组成。我们现有的代码库可能包含10万个(如果不是1 000 000)代码行。我们的模块也是多线程的。

我们链接到正确的库,以便我们使用nothrow new(lic.lib而不是licp.lib)。

大多数代码没有异常处理。编写代码时考虑到了这一点。

int *p = new int[size];
if (p == NULL)
{
   // handle this case...
   // most probably return an error code
}

char *q = new char[size];
if (q == NULL)
{
    delete[] p;
   // handle this case...
   // most probably return an error code
}

我们也使用RAII技术。例如,我们在堆栈上创建了一个自动等待并释放临界区的对象。

我们希望提高SDK的稳定性。我们考虑添加异常处理,但我不相信这是提高稳定性的正确方法。我不得不承认我对EH没有多少经验。

通常,代码检查除以0或在解除引用之前检查NULL指针。但是,仍然会发生这样的情况。由于除以零或解除引用NULL指针不会抛出异常,我想知道它有多少用于通过10万行代码并添加异常处理,这将改变工作流程并且如果不处理可能导致内存泄漏正常。我尝试了SEH,但我认为开始使用SEH并不是有意义的,而且它是微软特有的,不是吗?

在我看来,我认为如果查看现有代码更简单,只需检查可能已经错过的可能崩溃,例如除零。

另外,如果我要添加异常处理,我将如何处理?一次修改所有模块或从下到上开始(意思是,如果模块A调用模块B调用模块C,我会修改C,然后B然后是A,因为我们经常发布我们的软件,我们可能只有时间在下一个版本之前修改C)。

谢谢!

c++ exception
3个回答
3
投票

我想知道将异常处理添加到现有代码的优缺点。

你没有准确地通过“异常处理”来表达你的意思,所以我将从一些基本的东西开始:标准C ++(你将问题标记为c++)要求你编写“处理异常”的代码,除了琐碎的应用程序之外的所有应用程序,否则你的代码有问题。允许C ++标准库的各个部分抛出异常,包括示例代码使用的new。因此,您的代码可能已经有可能在其中抛出异常,它必须“处理”。在那种情况下会发生什么?基本上,你必须写“exception safe code”。

  • 程序在异常情况下泄漏资源是错误的。你使用RAII,所以你应该没问题。
  • 抛出异常后,任何对象进入不一致状态都是错误的。确保这可能更棘手。

您应该首先关注使代码异常安全。


1
投票

使用Legacy代码,您应该在计划许可的情况下在几个地方引入异常处理;代码中访问量最少的区域(以减少代码库其余部分出错的风险)或者产生最大利益的地方(政治错误位置)。

我不建议拖延遗留项目只是为了在任何地方添加异常处理。遗留代码最难的部分是修改它并使其保持工作。毕竟,它已经过测试,其行为已有详细记录。


0
投票

我同意Raedwald的观点,如果你使用C ++而没有非常谨慎的编码标准来避免EH(例如:使用nothrow new,避免标准容器等),我假设遗留代码没有,那么代码已经被破坏了并且如果你依赖bad_alloc,它可能已经泄漏并且已经出现了零星的事情,例如bad_castdynamic_cast已经遇到的例外情况。

也就是说,从遗留代码库的非常务实的角度来看,遗留代码可能会成功逃脱它。毕竟,有多少非平凡的应用程序可以从bad_alloc异常中优雅地恢复,而无需非常明确地控制内存分配?并不多,也不会导致整个世界戛然而止。

所以我实际上不建议重写遗留代码以尝试捕获异常并在任何地方使用RAII。您可能会在代码中使用RAII,您必须修改它,但我会尝试寻找不要过多更改它的原因。为它编写测试并尝试稳定它并将其变成黑盒子;一个功能库,可以使用,无需维护和更改,同时无限期地浏览无尽的LOC。

现在,我在这里投入并重新启动这个旧线程的主要原因(道歉!)是因为这个评论:

通常,代码检查除以0或在解除引用之前检查NULL指针。但是,仍然会发生这样的情况。由于除以零或取消引用NULL指针不会抛出异常[...]

我强烈认为,你不应该回应空指针访问或除零之类的事情,因为那些是程序员错误。除非你在一个关键任务软件中工作,你想要优雅地恢复,即使软件是错误的,以便试图降低生命成本或类似的风险,你也不希望应用程序优雅地恢复编程错误的事件。你通常不想这样做的原因是因为它有隐藏错误的缺点,使它们保持沉默,允许用户忽略和解决它们,甚至可能甚至不用报告它们。

相反,对于程序员的错误,你通常应该支持assert,它根本不涉及异常。如果断言失败,则调试版本中的软件将停止运行,并且通常会显示一条错误消息,告诉您断言失败到精确的代码行。这通常是在响应错误报告时运行调试器时检测和修复这些编程错误的最快的赌注,所以请随意使用assert

例外对于程序员控制之外的外部异常事件最有用。一个例子是读取一个结果证明是腐败的文件。这不属于程序员的控制范围,所以它适合抛出和恢复。另一个例子是无法连接到服务器,用户干扰应该完成的操作的中止按钮等等。这些是异常的外部输入事件,而不是程序员错误。

从程序员错误中恢复的最佳方法,如空指针访问和除零,首先检测它们(assert很方便),编写测试来重现它,修复它并让测试通过,然后调用它一天,而不是抛出异常并捕获它们,同时将错误留在那里。

© www.soinside.com 2019 - 2024. All rights reserved.