C ++新手问题 - 使用try,throw,catch进行基本错误处理

时间:2011-01-25 11:41:57

标签: c++ exception-handling

我正在尝试理解C ++中的错误处理。

我已经读过使用try,throw,catch比使用带有返回值的if语句更好的样式和更简单。但我不确定我是否真的理解如何尝试,抛出,抓住工作。我在下面做了一个简单的例子,很高兴获得有关任何问题或不良风格的反馈。我的目标是从检查另一个计算结果的示例中创建一个函数。

以下是关于try,throw,catch的问题: (1)捕获声明是否应包含在我的功能中?或者它应该在其他地方,例如在main()或初始计算的函数中?

(2)使用try,catch,throw这个简单的东西(我希望改进我的风格)是否过度了?

(3)如果有错误,我想终止程序。我该怎么办?或者“捕获”是否意味着这是自动完成的?

(4)我不明白使用cerr。为什么不使用cout?我在这里正确使用过cerr吗?我是否也应该在if / else语句中使用它?

非常感谢您的帮助。

以下是我做的例子:

double calculated = 10.2; // from previous calculation
double tolerance = 0.3; // I can set this in this function
double valueWanted = 10.0; // from previous calculation

const int calcError = 5; // I picked this number randomly to use for indicating an error

try
{
   if (fabs(fTargetValue - fCalculated) <= fTolerance)
     cout << "Result is within range.";

   else
     cout << "Failed.";
     throw calcError;
}

catch (const int calcError)
{
   cerr << "The calculation failed.\n" << endl;
}

5 个答案:

答案 0 :(得分:4)

那是很多问题。我会尝试给你一些提示:

(1)不要在函数中包含try-catch。抛出异常是为了告诉外面世界发生了什么事。如果你可以处理你的函数中的问题,不要扔掉^^一个好的错误处理通常是在ASAP(在调用者中)或远在main中的通用处理程序中捕获错误,以处理正常未处理的错误

(2)根据经验,对......特殊事物使用例外。错误是特殊事物的良好候选者。在数学库中可能会出现溢出或除零等异常。你必须决定,但总的来说处理异常错误是好的。

(3)抓住并不意味着你的计划将结束。事实上恰恰相反。通过捕获异常,你说你将处理问题^^如果你想终止,简单程序中的一个简单方法是不捕获异常,因为未捕获异常的默认行为是程序终止^^相反,你可以在某个catch块中显式终止你的程序。

(4)cerr就像cout一样,但它是一个不同的文件描述符。这意味着外部程序可以区分cerr和cout。它用于错误,但这对外部程序来说并不重要。

MY2C

答案 1 :(得分:2)

好的,首先你的例子会抛出,因为你在else之后没有范围括号。因此,无论结果是否在范围内,每次都只执行cout << "Failed.";并执行throw calcError。将其更改为:

else
{
    cout << "Failed.";
    throw calcError;
}

如果抛出异常,代码将在您定义的catch块内开始,说明计算失败。

如果结果在范围内(永远不会调用throw),代码将在 catch块后直接开始执行。

当你抛出一个类型时,那个类型会到达catch处理程序。这允许您为不同类型定义catch处理程序。在这种情况下,您正在抛出(和捕获)const int。这一切都很好。通常,我们抛出std::exception或其衍生物。您自己的异常类可以包含与错误相关的信息。在您的情况下,您可以包含一个超出范围的简单消息,或者确实包含失败的const int。

答案 2 :(得分:2)

  1. catch语句应该在抛出的第一个函数中(可能在抛出的函数中),该函数可以从异常中恢复并允许程序正常继续。

  2. 是的,如果您希望抓住它,那就没有任何意义。此外,您的正常程序流程不应抛出。根据经验,只有当你遇到一种你并不真正期望的情况时才会抛出。例外被称为例外,因为它们发生在特殊情况下。通常,在与程序环境交互时,使用异常的好时机。您通常希望某些事情能够发挥作用,例如能够分配内存,打开文件,从网络设备接收完整的数据包等。所有这些情况都会导致抛出异常。此外,如果您的程序收到输入,它应该最初验证它。但是,稍后,在处理过程中,如果应该已经被验证拒绝的数据有问题,例如由于奇怪的输入数据而导致零除,这也是一种特殊情况。如果您在预期的事情发生时过多地依赖异常,那么程序的流程和逻辑就会变得过于难以推理,并且程序维护也会变得不必要。

  3. 如果出现错误,请不要抓住。如果没有catch,异常将一直到你的main函数,然后从那里转到运行时将终止你的程序。并且,对于某些O.S.s,例如windows,这将导致创建一个minidump文件,您可以使用它来调试程序,以找出导致它终止的异常。

  4. cerr和cout只为您提供了两种从程序输出信息的方法。如果另一个程序使用程序的输出来执行某些操作,它将读取cout并期望理解它。这意味着如果你想写出消费程序无法理解的错误或警告,你必须将它们写入cerr,这样你就不会混淆正在阅读程序正常cout输出的第二个程序。

答案 3 :(得分:2)

C ++标准有一些可以从中派生的异常类。我建议你这样做而不是投掷和捕捉POD。它也不难,并且会改进(并指定错误的类型),如此

class CalculationError : std::invalid_argument
{
public:
    CalculationError(std::string const& msg)
        : std::invalid_argument(msg)
    {}
};

要快速了解异常层次结构,请转到http://www.richelbilderbeek.nl/CppExceptionHierarchy.htm

问题是:当您抛出POD类型时,没有附加消息。抛出异常的重要部分是能够写出可能出错的消息,以及如何解决它。抛出int时无法做到这一点。

C ++中有三个输出流:log,cerr和cout。它们中的每一个都以不同的方式表示,这意味着,在启动程序时,您可以使用命令行过滤掉每个流。这对于调试非常有用,因为您可以通过cerr进行过滤,看看您的程序是否未通过测试。

示例:my_program > out.txt 2> log.txt(cout转到out.txt,其他转到log.txt)

但是,我建议不要只是咒骂。通常,捕获点是反转程序状态!例如,如果您尝试分配动态数组,并且失败,那么catch将负责在重新抛出之前再次破坏数组。否则,你会遇到很多内存泄漏等等。

值得注意的是,一旦被抓住,异常就会被“吞噬”。如果你不能或不想在这里处理错误,最好写

catch(/* Error to be caught */)
{
    throw; // Rethrows original exception, propagating it upwards
}

如果你想要一些关于此的好文献,Herb Sutter写了一本名为Exceptional C++的书,它以实用和启发的方式涵盖了异常安全(imo)。如果您想知道何时以及为什么需要抛出异常,那么绝对值得一试。

希望这有帮助!

答案 4 :(得分:1)

难道你没有忘记围绕其他案件的阻拦

try
{
   if (fabs(fTargetValue - fCalculated) <= fTolerance)
     cout << "Result is within range.";

   else {
     cout << "Failed.";
     throw calcError;
   }

}