如何抛出,试试{} catch {}应该在现实世界中使用?

时间:2011-10-01 16:27:04

标签: c++ exception try-catch throw

我的意思是,我知道关于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,任何好建议的真正好处?

8 个答案:

答案 0 :(得分:6)

处理错误的每个网站都需要try - catch阻止。这一切都取决于你的设计,但我怀疑你需要在每个rotate_in_place呼叫站点处理错误,你可能在大多数时间都不会向上传播。

打印错误并使用exit是不好的,原因有三:

  1. 您无法处理错误。 exit未处理(除非在错误绝对严重时执行,但您的函数无法知道 - 调用者可能有办法恢复)。
  2. 您正在通过写入硬编码流来扩展该功能的职责,该流甚至可能不可用(这是rotate_in_place,而不是rotate_in_place_and_print_errors_and_kill_the_program_if_something_is_wrong) - 这会损害可重用性。
  3. 使用这种方法丢失所有调试信息(你可以从未处理的异常中生成堆栈跟踪,你不能对每次执行的函数执行任何操作 - 未处理的异常是一个错误,但这是一个你可以遵循的错误来源)。

答案 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)

使用异常处理的要点遵循以下简单规则:

  • 一旦由于用户输入错误(内部逻辑应通过断言/记录处理)发生任何不良事件,就抛出异常。尽可能快地抛出:与.Net相比,C ++异常通常相当便宜。
  • 如果无法处理错误,请传播异常。这几乎总是意味着。

要记住的事情是:异常应该到达可以处理的程度。这可能意味着一个带有一些错误格式的对话框,或者这可能意味着一些不重要的逻辑将不会被执行等等。

答案 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:如果您使用异常,则代码的异常生成部分不需要指定如何处理异常情况。这发生在外部程序中,可以根据代码的使用方式进行更改。