我的意思是,我知道关于throw的所有语言规则,尝试{} catch {},但我不确定我是否在现实世界中正确使用它们。请参阅以下示例:
我们有大量的科学代码可以完成各种图像处理工作,最近我们决定对其进行修改并使其更加强大。经常使用的一个例程是 void rotate_in_place(float * image,image_size sz);
为了使其更加健壮,我们在代码的开头添加了一些健全性检查:
void rotate_in_place(float* image, image_size sz) {
// rotate_in_place does not support non-square image;
if (sz.nx != sz.ny) throw NonSquareImageError;
// rotate_in_place does not support image too small or too large
if (sz.nx <= 2 || sz.nx > 1024) throw WrongImageSizeError;
// Real rode here
.....
}
现在的问题是rotate_in_place()在1000多个地方使用,我应该用try {} catch {}包装rotate_in_place()的每次调用,这对我来说会让代码难以置信地膨胀。另一种可能性是不包装任何try {} catch {}并让程序退出,但这与仅使用
有什么不同if (sz.nx != sz.ny) {
cerr << "Error: non-squared image error!\n";
exit(0);
}
简而言之,我不太确定使用throw,try,catch,任何好建议的真正好处?
答案 0 :(得分:6)
处理错误的每个网站都需要try
- catch
阻止。这一切都取决于你的设计,但我怀疑你需要在每个rotate_in_place
呼叫站点处理错误,你可能在大多数时间都不会向上传播。
打印错误并使用exit
是不好的,原因有三:
exit
未处理(除非在错误绝对严重时执行,但您的函数无法知道 - 调用者可能有办法恢复)。rotate_in_place
,而不是rotate_in_place_and_print_errors_and_kill_the_program_if_something_is_wrong
) - 这会损害可重用性。答案 1 :(得分:5)
例外的一般规则是,“直接呼叫网站是否关心这里发生了什么?”如果呼叫站点确实关心,那么返回状态代码可能是有意义的。否则,投掷更有意义。
以这种方式考虑 - 当然,你的rotate in place方法有几个无效的参数类型,在这种情况下你应该抛出std::invalid_argument
。例如,rotate_in_place
的调用者不太可能想要处理或知道如何处理图像不是正方形的情况,因此可能更好地表示为异常。
另一种可能性是不要包装任何try {} catch {}并让它 程序退出,但这与仅使用
有什么不同if (sz.nx != sz.ny) { cerr << "Error: non-squared image error!\n"; exit(0); }
这是不同的,因为如果某人后来想要将您的功能放入GUI应用程序中,则他们不必根据错误终止程序。他们可以将该异常转化为适合用户或类似的东西。
它现在对您也有好处 - 即您不必将<iostream>
拉入该翻译单元只是为了写错误。
我通常使用这样的模式:
int realEntryPoint()
{
//Program goes here
}
int main()
{
//Allow the debugger to get the exception if this is a debug binary
#ifdef NDEBUG
try
#endif
{
return realEntryPoint();
}
#ifdef NDEBUG
catch (std::exception& ex)
{
std::cerr << "An exception was thrown: " << ex.what() << std::endl;
}
#endif
}
答案 2 :(得分:4)
现在的问题是在超过1000个地方使用了rotate_in_place(),我应该用
rotate_in_place()
包裹try{} catch {}
的每次调用,这对我来说会让代码难以置信地膨胀。
它会,并且它首先打败了使用例外的目的。
另一种可能性是不包装任何try {} catch {}并让程序退出,但这与仅使用[...]
有何不同
您可以随时更改异常处理的位置。如果在某些时候你找到了一个更好的地方来明智地处理错误(也许从中恢复),那么就是你放置catch
的地方。有时这就是你抛出异常的函数;有时它会在调用链中上升。
请将catch
全部放在main
中,以防万一。从std::runtime_error
等标准异常中获取异常会使这样做变得更容易。
答案 3 :(得分:3)
使用异常处理的要点遵循以下简单规则:
要记住的事情是:异常应该到达可以处理的程度。这可能意味着一个带有一些错误格式的对话框,或者这可能意味着一些不重要的逻辑将不会被执行等等。
答案 4 :(得分:1)
使用例外允许来电者决定如何处理错误。如果您直接在函数内调用exit
,则程序将退出,而调用者无法决定如何处理错误。此外,对于exit
,堆栈对象不会被解开。 : - (
答案 5 :(得分:1)
如果函数调用成功,则可以使rotate_in_place返回布尔值。并通过函数参数返回旋转的图像。
bool rotate_in_place(float* image, image_size sz, float** rotated_image) {
// rotate_in_place does not support non-square image;
if (sz.nx != sz.ny) return false;
// rotate_in_place does not support image too small or too large
if (sz.nx <= 2 || sz.nx > 1024) return false;
// Real rode here
.....
return true;
}
答案 6 :(得分:0)
取决于。
例外通常意味着被抓住/处理。在您的情况下,是否可以处理异常(例如,用户提供非方形图像,因此您要求他们再试一次)。但是,如果你无能为力,那么cerr
就是你要走的路。
答案 7 :(得分:0)
嗯,我同意真正使用Exceptions会导致代码膨胀。这是我不喜欢它们的主要原因。
无论如何,关于你的例子:抛出异常和使用exit()之间的关键区别在于,因为异常的处理发生(或者应该发生)在生成错误/异常的程序片段之外,您没有指定函数/类的用户如何处理错误。通过使用例外,您可以允许不同的处理方法,例如中止程序,报告错误甚至从某些错误中恢复。
TLDNR:如果您使用异常,则代码的异常生成部分不需要指定如何处理异常情况。这发生在外部程序中,可以根据代码的使用方式进行更改。