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

时间:2011-11-01 15:16:42

标签: c++ exception

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

我在一个控制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。)

谢谢!

3 个答案:

答案 0 :(得分:3)

  

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

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

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

首先应该专注于使代码异常安全。

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

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

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

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

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

  

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

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

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

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

从程序员错误中恢复如空指针访问和除零的最佳方法是首先检测它们(assert是否方便),编写测试以重现它,修复它并让测试通过,并把它称为一天,不要抛出异常并抓住它们,同时将这些错误留在那里。