可能是重复问题:
通过断言还是异常进行设计契约测试?
当将空指针作为输出参数传递给函数时,处理的首选方法是什么?我可以使用ASSERT,但我觉得让库崩溃程序不好。相反,我考虑使用异常。
可能是重复问题:
通过断言还是异常进行设计契约测试?
当将空指针作为输出参数传递给函数时,处理的首选方法是什么?我可以使用ASSERT,但我觉得让库崩溃程序不好。相反,我考虑使用异常。
抛出异常!那就是它们存在的目的。这样,你库的用户可以决定是否优雅地处理异常或崩溃。
另一个具体的解决方案是返回有效类型的无效值,例如对于返回索引的方法,返回负整数,但只能在特定情况下使用。
assert
进行检查,在发布模式下可能会崩溃(未定义行为),或者使用指定的断言宏,在发布模式下仍然处于活动状态。 strlen
等函数采用后一种哲学,而vector<> :: at
等函数采用前一种哲学。后者明确规定了超出范围的值的行为,而前者仅声明传递空指针的行为未定义。try {
process(data);
} catch(NullPointerException &e) {
process(getNonNullData());
}
在我看来,这很丑陋。如果您在函数中断言指针为空,则此类代码将变为:
if(!data) {
process(getNonNullData());
} else {
process(data);
}
我认为这种方法更加优越,因为它不使用异常来控制流程(提供一个非空源作为参数)。如果您没有处理异常,那么您可能会在process
中失败,并且直接指向崩溃发生的文件和行号(并且使用调试器,您实际上可以获得堆栈跟踪)。
在我的应用程序中,我总是采用assert
路线。我的哲学是,空指针参数应该完全由非异常路径处理,或断言为非NULL。
两者都要做。
在开发过程中捕获的任何错误都会终止该进程,这将使开发人员明显意识到需要修复它。
如果一个错误在测试中被放行了,仍然有一个异常可以被强大的程序处理。
而这很容易放入宏中(必须是宏而不是内联,以便assert正确报告行号-感谢@RogerPate指出这一点):
#define require_not_null(ptr) \
do { assert(ptr); if (!(ptr)) throw std::logic_error("null ptr"); } while (0)
如果你抛出异常,客户端可以决定重新抛出、不处理异常、崩溃或调用退出或尝试恢复等操作。
如果你崩溃了,客户端也会跟着崩溃。
因此,抛出异常让你的客户端更有灵活性。
strlen()
。如果它引发了异常,你怎么可能处理它呢?而且在生产代码中,断言不会触发。唯一明智的做法是明确声明该函数不能使用NULL指针作为参数调用,这样做将导致未定义的行为。strlen
抛出异常会很奇怪。捕获它的异常有什么意义呢?但是以下语句并不排除断言(也不排除异常,因为未定义的行为可以做任何它想做的事情。但是如果调用代码不知道是否会抛出异常,那么抛出异常就变得毫无意义,因为没有人准备捕获它!):“唯一明智的做法是明确声明该函数不能使用NULL指针作为参数调用,这样做将导致未定义的行为。” - Johannes Schaub - litbcatch (...)
来放弃该请求而不会导致整个服务器崩溃并影响其他请求。现在,如果异常情况确实导致了异常,服务器就可以继续处理所有其他请求而不会崩溃。 - R Samuel Klatchko