我该如何处理断言失败?

时间:2012-08-21 21:51:49

标签: c++

是否有正确或可接受的方式来处理调试/释放模式中的断言失败?

例如: 假设我有一个返回std :: vector

的函数

我希望返回的向量的长度等于另一个对象的长度,我这样做:

std::vector<int> v = get_stuff();
ASSERT(v.size() == this->size() )
a = v[this->size() - 1 ];

现在如果没有触发断言,这段代码将正常工作,并且在调试中这可能会崩溃,但至少我会让断言失败警告我出错了。 问题是在发布模式下,会发生无声崩溃。 这是否意味着我还必须在发布代码中检查此错误然后处理它?它是可能的,但是我认为在它上面添加一个断言失败是没有意义的,因为它已被处理

5 个答案:

答案 0 :(得分:2)

assert的含义与您的想法不同。它不是一个例外的替代品,我认为这是你如何看待的。它就在那里,你可以在调试的早期发现问题。如果在调试中获得断言,则修复它。然后你测试。然后再次测试。并确保条件在释放时保持不变。如果确实......好吧,你有一个错误。

  

这是否意味着我还必须在发布代码中检查此错误然后处理它?

如果您希望它发生,是的。检查条件,抛出异常并精细处理。发送错误报告。写入日志文件。更新软件。

答案 1 :(得分:1)

断言是指必须始终为真的条件。不适用于异常处理。

它们反映了您在设计过程中所做的假设。如果get_stuff任何合法的可能返回错误大小的向量,则必须单独处理。

答案 2 :(得分:1)

您不仅限于C库提供的断言,而且还有一个额外的断言机制仍然可以在生产/发布版本中运行:

  • 使用C库assert进行昂贵的(CPU,缓存转换,数据库加载...)检查,确保您不希望减慢生产/发布版本的速度。
  • 使用您自己的断言机制来进行即使在生产/发布版本中也要运行的廉价或极其重要的测试,其中失败表明您的程序无法在此后正常运行:例如,如果数据结构是核心你的程序操作明显已损坏
  • 当您认为可以报告错误并返回有用状态以进行进一步工作时,使用例外/错误代码等,并且继续提供该服务是优先事项

所以,在你的例子中:

std::vector<int> v = get_stuff();
ASSERT(v.size() == this->size());
a = v[this->size() - 1];

您可以使用只有调试模式的ASSERT,也可以在生产中启动的ASSERT,或者跟随前者使用......

a = v.at(this->size() - 1);

...这样你就可以获得一个例外,如果问题出现在生产中,你可以抓住并处理。要获得异常处理案例的代码覆盖率,您需要为生产构建创建一个单元测试用例。

要记住的是找到一个真实且可维护的平衡:如果你在运行时错误处理中试图过于详尽,那么代码的大小和复杂性可能会达到5到10倍,而你的测试工作也会耗尽。因此,要选择处理的地方和程度。简单地断言和核心倾倒等相对简单:一个没有测试用例的衬里,可以更自由地使用。

答案 3 :(得分:0)

我的首选是包含ASSERT(用于调试),然后是处理错误的代码。在调试运行中,断言失败会使系统停止并让我立即看到问题。然后在发布版本中,处理错误情况的代码仍然运行。当然,必须考虑处理错误的代码。生成崩溃转储文件(Windows中的minidump,Linux中的核心转储)可能是有意义的。

答案 4 :(得分:0)

通常你必须在你的程序中“处理”三种错误,我个人更喜欢按照以下方式处理和分类它们(ymmv):

  • 预计会频繁发生的错误(例如,错误的用户输入)。它们可能(部分)可以恢复。我通常通过检查/错误代码处理它们,偶尔通过例外处理它们。
  • 检测到异常错误,但通常不会发生。我总是对它们使用例外,它们通常会使操作失败。
  • 可能不会发生的错误;这是“不可能”发生的。就像你的vector类一样,你会期望人们使用正确的索引。您不希望花费宝贵的周期来检查发布模式中的这些条件,但是当很容易检测到这些情况时,请在调试模式下执行此操作,以便为难以调试的崩溃提供更好的替代方案(例如,使用{ {1}}。

我通常使用断言测试前提条件,例如某些指针非空,或者值在范围内。我慷慨地撒了它们,所以在调试模式下(我的所有测试和单元测试都在运行中)可以找到这些错误。它有点像“检测未定义行为”模式,相当一些标准库有。